Unverified Commit 8004d107 by Michael Mifsud Committed by GitHub

Bump LibSass@3.5.3 (#2342)

* Bump LibSass@3.5.3

See https://github.com/sass/libsass/releases/tag/3.5.3

* Workaround sass-spec

A patch landed in sass-spec that is not compatible with 3.5.
parent 3ab7a16a
{
"name": "node-sass",
"version": "4.8.3",
"libsass": "3.5.2",
"libsass": "3.5.3",
"description": "Wrapper around libsass",
"license": "MIT",
"bugs": "https://github.com/sass/node-sass/issues",
......@@ -83,7 +83,7 @@
"object-merge": "^2.5.1",
"read-yaml": "^1.0.0",
"rimraf": "^2.5.2",
"sass-spec": "^3.5.1",
"sass-spec": "3.5.1",
"unique-temp-dir": "^1.0.0"
}
}
......@@ -25,63 +25,49 @@ else
AM_CXXFLAGS += -std=c++0x
endif
TEST_EXTENSIONS = .rb
if ENABLE_TESTS
noinst_PROGRAMS = tester
SASS_SASSC_PATH ?= $(top_srcdir)/sassc
SASS_SPEC_PATH ?= $(top_srcdir)/sass-spec
noinst_PROGRAMS = tester
tester_LDADD = src/libsass.la
tester_SOURCES = $(SASS_SASSC_PATH)/sassc.c
tester_VERSION ?= `cd "$(SASS_SASSC_PATH)" && ./version.sh`
tester_CFLAGS = $(AM_CFLAGS) -DSASSC_VERSION="\"$(tester_VERSION)\""
tester_CXXFLAGS = $(AM_CXXFLAGS) -DSASSC_VERSION="\"$(tester_VERSION)\""
tester_LDFLAGS = $(AM_LDFLAGS)
nodist_tester_SOURCES = $(SASS_SASSC_PATH)/sassc.c
SASS_SASSC_VERSION ?= `cd "$(SASS_SASSC_PATH)" && ./version.sh`
tester_CFLAGS = $(AM_CFLAGS) -DSASSC_VERSION="\"$(SASS_SASSC_VERSION)\""
tester_CXXFLAGS = $(AM_CXXFLAGS) -DSASSC_VERSION="\"$(SASS_SASSC_VERSION)\""
if ENABLE_COVERAGE
nodist_EXTRA_tester_SOURCES = non-existent-file-to-force-CXX-linking.cxx
endif
SASS_SASSC_PATH ?= $(top_srcdir)/sassc
SASS_SPEC_PATH ?= $(top_srcdir)/sass-spec
TESTS = \
$(SASS_SPEC_PATH)/spec/basic \
$(SASS_SPEC_PATH)/spec/css \
$(SASS_SPEC_PATH)/spec/extend-tests \
$(SASS_SPEC_PATH)/spec/extends \
$(SASS_SPEC_PATH)/spec/libsass \
$(SASS_SPEC_PATH)/spec/libsass-closed-issues \
$(SASS_SPEC_PATH)/spec/maps \
$(SASS_SPEC_PATH)/spec/misc \
$(SASS_SPEC_PATH)/spec/regressions \
$(SASS_SPEC_PATH)/spec/scss \
$(SASS_SPEC_PATH)/spec/scss-tests \
$(SASS_SPEC_PATH)/spec/types
TESTS = $(SASS_SPEC_PATH)/sass-spec.rb
RB_LOG_COMPILER = ./script/tap-runner
AM_RB_LOG_FLAGS = $(RUBY)
SASS_TEST_FLAGS = -V 3.5 --impl libsass
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) ./script/tap-driver
AM_LOG_FLAGS = -c ./tester $(LOG_FLAGS)
if USE_TAP
AM_LOG_FLAGS += -t
SASS_TEST_FLAGS += -t | tapout
LOG_COMPILER = ./script/tap-runner $(RUBY) $(SASS_SPEC_PATH)/sass-spec.rb
else
LOG_COMPILER = $(RUBY) $(SASS_SPEC_PATH)/sass-spec.rb
endif
SASS_TEST_FLAGS += -r $(SASS_SPEC_PATH)
SASS_TEST_FLAGS += -c $(top_srcdir)/tester$(EXEEXT)
AM_TESTS_ENVIRONMENT = TEST_FLAGS='$(SASS_TEST_FLAGS)'
SASS_TESTER = $(RUBY) $(SASS_SPEC_PATH)/sass-spec.rb
SASS_TESTER += -c $(top_srcdir)/tester$(EXEEXT)
test:
$(SASS_TESTER) $(LOG_FLAGS) $(SASS_SPEC_PATH) $(SASS_TEST_FLAGS)
$(SASS_TESTER) $(SASS_TEST_FLAGS)
test_build:
$(SASS_TESTER) $(LOG_FLAGS) $(SASS_SPEC_PATH) $(SASS_TEST_FLAGS)
$(SASS_TESTER) $(SASS_TEST_FLAGS)
test_full:
$(SASS_TESTER) --run-todo $(LOG_FLAGS) $(SASS_SPEC_PATH) $(SASS_TEST_FLAGS)
$(SASS_TESTER) --run-todo $(SASS_TEST_FLAGS)
test_probe:
$(SASS_TESTER) --probe-todo $(LOG_FLAGS) $(SASS_SPEC_PATH) $(SASS_TEST_FLAGS)
$(SASS_TESTER) --probe-todo $(SASS_TEST_FLAGS)
.PHONY: test test_build test_full test_probe
endif
......
LibSass - Sass compiler written in C++
======================================
Welcome to the LibSass documentation!
Currently maintained by Marcel Greter ([@mgreter]) and Michael Mifsud ([@xzyfer])
Originally created by Aaron Leung ([@akhleung]) and Hampton Catlin ([@hcatlin])
## First Off
LibSass is just a library. To run the code locally (i.e. to compile your stylesheets), you need an implementer. SassC (get it?) is an implementer written in C. There are a number of other implementations of LibSass - for example Node. We encourage you to write your own port - the whole point of LibSass is that we want to bring Sass to many other languages, not just Ruby!
[![Unix CI](https://travis-ci.org/sass/libsass.svg?branch=master)](https://travis-ci.org/sass/libsass "Travis CI")
[![Windows CI](https://ci.appveyor.com/api/projects/status/github/sass/libsass?svg=true)](https://ci.appveyor.com/project/sass/libsass/branch/master "Appveyor CI")
[![Coverage Status](https://img.shields.io/coveralls/sass/libsass.svg)](https://coveralls.io/r/sass/libsass?branch=feature%2Ftest-travis-ci-3 "Code coverage of spec tests")
[![Percentage of issues still open](http://isitmaintained.com/badge/open/sass/libsass.svg)](http://isitmaintained.com/project/sass/libsass "Percentage of issues still open")
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/sass/libsass.svg)](http://isitmaintained.com/project/sass/libsass "Average time to resolve an issue")
[![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=283068)](https://www.bountysource.com/trackers/283068-libsass?utm_source=283068&utm_medium=shield&utm_campaign=TRACKER_BADGE "Bountysource")
[![Join us](https://libsass-slack.herokuapp.com/badge.svg)](https://libsass-slack.herokuapp.com/ "Slack communication channels")
We're working hard on moving to full parity with Ruby Sass... learn more at the [The-LibSass-Compatibility-Plan](compatibility-plan.md)!
### Implementing LibSass
[LibSass](https://github.com/sass/libsass "LibSass GitHub Project") is just a library!
If you want to use LibSass to compile Sass, you need an implementer. Some
implementations are only bindings into other programming languages. But most also
ship with a command line interface (CLI) you can use directly. There is also
[SassC](https://github.com/sass/sassc), which is the official lightweight
CLI tool built by the same people as LibSass.
If you're interested in implementing LibSass in your own project see the [API Documentation](api-doc.md) which now includes implementing
your own [Sass functions](api-function.md). You may wish to [look at other implementations](implementations.md) for your language of choice.
Or make your own!
### Excerpt of "sanctioned" implementations:
### Contributing to LibSass
- https://github.com/sass/node-sass (Node.js)
- https://github.com/sass/perl-libsass (Perl)
- https://github.com/sass/libsass-python (Python)
- https://github.com/wellington/go-libsass (Go)
- https://github.com/sass/sassc-ruby (Ruby)
- https://github.com/sass/libsass-net (C#)
- https://github.com/medialize/sass.js (JS)
- https://github.com/bit3/jsass (Java)
| Issue Tracker | Issue Triage | Community Guidelines |
|-------------------|----------------------------------|-----------------------------|
| We're always needing help, so check out our issue tracker, help some people out, and read our article on [Contributing](contributing.md)! It's got all the details on what to do! | To help understand the process of triaging bugs, have a look at our [Issue-Triage](triage.md) document. | Oh, and don't forget we always follow [[Sass Community Guidelines|http://sass-lang.com/community-guidelines]]. Be nice and everyone else will be nice too! |
This list does not say anything about the quality of either the listed or not listed [implementations](docs/implementations.md)!
The authors of the listed projects above are just known to work regularly together with LibSass developers.
About
-----
LibSass is a C++ port of the original Ruby Sass CSS compiler with a [C API](docs/api-doc.md).
We coded LibSass with portability and efficiency in mind. You can expect LibSass to be a lot
faster than Ruby Sass and on par or faster than the best alternative CSS compilers around.
Developing
----------
As noted above, the LibSass repository does not contain any binaries or other way to execute
LibSass. Therefore, you need an implementer to develop LibSass. Easiest is to start with
the official [SassC](http://github.com/sass/sassc) CLI wrapper. It is *guaranteed* to compile
with the latest code in LibSass master, since it is also used in the CI process. There is no
limitation here, as you may use any other LibSass implementer to test your LibSass branch!
Testing
-------
Since LibSass is a pure library, tests are run through the [Sass-Spec](https://github.com/sass/sass-spec)
project using the [SassC](http://github.com/sass/sassc) CLI wrapper. To run the tests against LibSass while
developing, you can run `./script/spec`. This will clone SassC and Sass-Spec under the project folder and
then run the Sass-Spec test suite. You may want to update the clones to ensure you have the latest version.
Note that the scripts in the `./script` folder are mainly intended for our CI needs.
Building
--------
To build LibSass you need GCC 4.6+ or Clang/LLVM. If your OS is older, you may need to upgrade
them first (or install clang as an alternative). On Windows, you need MinGW with GCC 4.6+ or VS 2013
Update 4+. It is also possible to build LibSass with Clang/LLVM on Windows with various build chains
and/or command line interpreters.
See the [build docs for further instructions](docs/build.md)!
Compatibility
-------------
Current LibSass 3.4 should be compatible with Sass 3.4. Please refer to the [sass compatibility
page](http://sass-compatibility.github.io/) for a more detailed comparison. But note that there
are still a few incomplete edges which we are aware of. Otherwise LibSass has reached a good level
of stability, thanks to our ever growing [Sass-Spec test suite](https://github.com/sass/sass-spec).
About Sass
----------
Sass is a CSS pre-processor language to add on exciting, new, awesome features to CSS. Sass was
the first language of its kind and by far the most mature and up to date codebase.
Sass was originally conceived of by the co-creator of this library, Hampton Catlin ([@hcatlin]).
Most of the language has been the result of years of work by Natalie Weizenbaum ([@nex3]) and
Chris Eppstein ([@chriseppstein]).
For more information about Sass itself, please visit http://sass-lang.com
Initial development of LibSass by Aaron Leung and Hampton Catlin was supported by [Moovweb](http://www.moovweb.com).
Licensing
---------
Our [MIT license](LICENSE) is designed to be as simple and liberal as possible.
[@hcatlin]: https://github.com/hcatlin
[@akhleung]: https://github.com/akhleung
[@chriseppstein]: https://github.com/chriseppstein
[@nex3]: https://github.com/nex3
[@mgreter]: https://github.com/mgreter
[@xzyfer]: https://github.com/xzyfer
Please refer to the steps on [Building LibSass](build.md)
## Example main.c
```C
#include <stdio.h>
#include "sass/context.h"
int main( int argc, const char* argv[] )
{
// get the input file from first argument or use default
const char* input = argc > 1 ? argv[1] : "styles.scss";
// create the file context and get all related structs
struct Sass_File_Context* file_ctx = sass_make_file_context(input);
struct Sass_Context* ctx = sass_file_context_get_context(file_ctx);
struct Sass_Options* ctx_opt = sass_context_get_options(ctx);
// configure some options ...
sass_option_set_precision(ctx_opt, 10);
// context is set up, call the compile step now
int status = sass_compile_file_context(file_ctx);
// print the result or the error to the stdout
if (status == 0) puts(sass_context_get_output_string(ctx));
else puts(sass_context_get_error_message(ctx));
// release allocated memory
sass_delete_file_context(file_ctx);
// exit status
return status;
}
```
### Compile main.c
```bash
gcc -c main.c -o main.o
gcc -o sample main.o -lsass
echo "foo { margin: 21px * 2; }" > foo.scss
./sample foo.scss => "foo { margin: 42px }"
```
```C
// Input behaviours
enum Sass_Input_Style {
SASS_CONTEXT_NULL,
SASS_CONTEXT_FILE,
SASS_CONTEXT_DATA,
SASS_CONTEXT_FOLDER
};
// sass config options structure
struct Sass_Inspect_Options {
// Output style for the generated css code
// A value from above SASS_STYLE_* constants
enum Sass_Output_Style output_style;
// Precision for fractional numbers
int precision;
};
// sass config options structure
struct Sass_Output_Options : Sass_Inspect_Options {
// String to be used for indentation
const char* indent;
// String to be used to for line feeds
const char* linefeed;
// Emit comments in the generated CSS indicating
// the corresponding source line.
bool source_comments;
};
// sass config options structure
struct Sass_Options : Sass_Output_Options {
// embed sourceMappingUrl as data uri
bool source_map_embed;
// embed include contents in maps
bool source_map_contents;
// create file urls for sources
bool source_map_file_urls;
// Disable sourceMappingUrl in css output
bool omit_source_map_url;
// Treat source_string as sass (as opposed to scss)
bool is_indented_syntax_src;
// The input path is used for source map
// generation. It can be used to define
// something with string compilation or to
// overload the input file path. It is
// set to "stdin" for data contexts and
// to the input file on file contexts.
char* input_path;
// The output path is used for source map
// generation. LibSass will not write to
// this file, it is just used to create
// information in source-maps etc.
char* output_path;
// Colon-separated list of paths
// Semicolon-separated on Windows
// Maybe use array interface instead?
char* include_path;
char* plugin_path;
// Include paths (linked string list)
struct string_list* include_paths;
// Plugin paths (linked string list)
struct string_list* plugin_paths;
// Path to source map file
// Enables source map generation
// Used to create sourceMappingUrl
char* source_map_file;
// Directly inserted in source maps
char* source_map_root;
// Custom functions that can be called from sccs code
Sass_Function_List c_functions;
// Callback to overload imports
Sass_Importer_List c_importers;
// List of custom headers
Sass_Importer_List c_headers;
};
// base for all contexts
struct Sass_Context : Sass_Options
{
// store context type info
enum Sass_Input_Style type;
// generated output data
char* output_string;
// generated source map json
char* source_map_string;
// error status
int error_status;
char* error_json;
char* error_text;
char* error_message;
// error position
char* error_file;
size_t error_line;
size_t error_column;
const char* error_src;
// report imported files
char** included_files;
};
// struct for file compilation
struct Sass_File_Context : Sass_Context {
// no additional fields required
// input_path is already on options
};
// struct for data compilation
struct Sass_Data_Context : Sass_Context {
// provided source string
char* source_string;
char* srcmap_string;
};
// Compiler states
enum Sass_Compiler_State {
SASS_COMPILER_CREATED,
SASS_COMPILER_PARSED,
SASS_COMPILER_EXECUTED
};
// link c and cpp context
struct Sass_Compiler {
// progress status
Sass_Compiler_State state;
// original c context
Sass_Context* c_ctx;
// Sass::Context
Sass::Context* cpp_ctx;
// Sass::Block
Sass::Block_Obj root;
};
```
## Introduction
LibSass wouldn't be much good without a way to interface with it. These
interface documentations describe the various functions and data structures
available to implementers. They are split up over three major components, which
have all their own source files (plus some common functionality).
- [Sass Context](api-context.md) - Trigger and handle the main Sass compilation
- [Sass Value](api-value.md) - Exchange values and its format with LibSass
- [Sass Function](api-function.md) - Get invoked by LibSass for function statments
- [Sass Importer](api-importer.md) - Get invoked by LibSass for @import statments
### Basic usage
First you will need to include the header file!
This will automatically load all other headers too!
```C
#include "sass/context.h"
```
## Basic C Example
```C
#include <stdio.h>
#include "sass/context.h"
int main() {
puts(libsass_version());
return 0;
}
```
```bash
gcc -Wall version.c -lsass -o version && ./version
```
## More C Examples
- [Sample code for Sass Context](api-context-example.md)
- [Sample code for Sass Value](api-value-example.md)
- [Sample code for Sass Function](api-function-example.md)
- [Sample code for Sass Importer](api-importer-example.md)
## Compiling your code
The most important is your sass file (or string of sass code). With this, you
will want to start a LibSass compiler. Here is some pseudocode describing the
process. The compiler has two different modes: direct input as a string with
`Sass_Data_Context` or LibSass will do file reading for you by using
`Sass_File_Context`. See the code for a list of options available
[Sass_Options](https://github.com/sass/libsass/blob/36feef0/include/sass/interface.h#L18)
**Building a file compiler**
context = sass_make_file_context("file.scss")
options = sass_file_context_get_options(context)
sass_option_set_precision(options, 1)
sass_option_set_source_comments(options, true)
sass_file_context_set_options(context, options)
compiler = sass_make_file_compiler(sass_context)
sass_compiler_parse(compiler)
sass_compiler_execute(compiler)
output = sass_context_get_output_string(context)
// Retrieve errors during compilation
error_status = sass_context_get_error_status(context)
json_error = sass_context_get_error_json(context)
// Release memory dedicated to the C compiler
sass_delete_compiler(compiler)
**Building a data compiler**
context = sass_make_data_context("div { a { color: blue; } }")
options = sass_data_context_get_options(context)
sass_option_set_precision(options, 1)
sass_option_set_source_comments(options, true)
sass_data_context_set_options(context, options)
compiler = sass_make_data_compiler(context)
sass_compiler_parse(compiler)
sass_compiler_execute(compiler)
output = sass_context_get_output_string(context)
// div a { color: blue; }
// Retrieve errors during compilation
error_status = sass_context_get_error_status(context)
json_error = sass_context_get_error_json(context)
// Release memory dedicated to the C compiler
sass_delete_compiler(compiler)
## Sass Context Internals
Everything is stored in structs:
```C
struct Sass_Options;
struct Sass_Context : Sass_Options;
struct Sass_File_context : Sass_Context;
struct Sass_Data_context : Sass_Context;
```
This mirrors very well how `libsass` uses these structures.
- `Sass_Options` holds everything you feed in before the compilation. It also hosts
`input_path` and `output_path` options, because they are used to generate/calculate
relative links in source-maps. The `input_path` is shared with `Sass_File_Context`.
- `Sass_Context` holds all the data returned by the compilation step.
- `Sass_File_Context` is a specific implementation that requires no additional fields
- `Sass_Data_Context` is a specific implementation that adds the `input_source` field
Structs can be down-casted to access `context` or `options`!
## Memory handling and life-cycles
We keep memory around for as long as the main [context](api-context.md) object
is not destroyed (`sass_delete_context`). LibSass will create copies of most
inputs/options beside the main sass code. You need to allocate and fill that
buffer before passing it to LibSass. You may also overtake memory management
from libsass for certain return values (i.e. `sass_context_take_output_string`).
```C
// to allocate buffer to be filled
void* sass_alloc_memory(size_t size);
// to allocate a buffer from existing string
char* sass_copy_c_string(const char* str);
// to free overtaken memory when done
void sass_free_memory(void* ptr);
```
## Miscellaneous API functions
```C
// Some convenient string helper function
char* sass_string_unquote (const char* str);
char* sass_string_quote (const char* str, const char quote_mark);
// Get compiled libsass version
const char* libsass_version(void);
// Implemented sass language version
// Hardcoded version 3.4 for time being
const char* libsass_language_version(void);
```
## Common Pitfalls
**input_path**
The `input_path` is part of `Sass_Options`, but it also is the main option for
`Sass_File_Context`. It is also used to generate relative file links in source-
maps. Therefore it is pretty usefull to pass this information if you have a
`Sass_Data_Context` and know the original path.
**output_path**
Be aware that `libsass` does not write the output file itself. This option
merely exists to give `libsass` the proper information to generate links in
source-maps. The file has to be written to the disk by the
binding/implementation. If the `output_path` is omitted, `libsass` tries to
extrapolate one from the `input_path` by replacing (or adding) the file ending
with `.css`.
## Error Codes
The `error_code` is integer value which indicates the type of error that
occurred inside the LibSass process. Following is the list of error codes along
with the short description:
* 1: normal errors like parsing or `eval` errors
* 2: bad allocation error (memory error)
* 3: "untranslated" C++ exception (`throw std::exception`)
* 4: legacy string exceptions ( `throw const char*` or `std::string` )
* 5: Some other unknown exception
Although for the API consumer, error codes do not offer much value except
indicating whether *any* error occurred during the compilation, it helps
debugging the LibSass internal code paths.
## Real-World Implementations
The proof is in the pudding, so we have highlighted a few implementations that
should be on par with the latest LibSass interface version. Some of them may not
have all features implemented!
1. [Perl Example](https://github.com/sass/perl-libsass/blob/master/lib/CSS/Sass.xs)
2. [Go Example](https://godoc.org/github.com/wellington/go-libsass#example-Compiler--Stdin)
3. [Node Example](https://github.com/sass/node-sass/blob/master/src/binding.cpp)
## ABI forward compatibility
We use a functional API to make dynamic linking more robust and future
compatible. The API is not yet 100% stable, so we do not yet guarantee
[ABI](https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) forward
compatibility.
## Plugins (experimental)
LibSass can load plugins from directories. Just define `plugin_path` on context
options to load all plugins from the directories. To implement plugins, please
consult the following example implementations.
- https://github.com/mgreter/libsass-glob
- https://github.com/mgreter/libsass-math
- https://github.com/mgreter/libsass-digest
## Internal Structs
- [Sass Context Internals](api-context-internal.md)
- [Sass Value Internals](api-value-internal.md)
- [Sass Function Internals](api-function-internal.md)
- [Sass Importer Internals](api-importer-internal.md)
## Example main.c
```C
#include <stdio.h>
#include <stdint.h>
#include "sass/context.h"
union Sass_Value* call_fn_foo(const union Sass_Value* s_args, Sass_Function_Entry cb, struct Sass_Compiler* comp)
{
// get context/option struct associated with this compiler
struct Sass_Context* ctx = sass_compiler_get_context(comp);
struct Sass_Options* opts = sass_compiler_get_options(comp);
// get information about previous importer entry from the stack
Sass_Import_Entry import = sass_compiler_get_last_import(comp);
const char* prev_abs_path = sass_import_get_abs_path(import);
const char* prev_imp_path = sass_import_get_imp_path(import);
// get the cookie from function descriptor
void* cookie = sass_function_get_cookie(cb);
// we actually abuse the void* to store an "int"
return sass_make_number((intptr_t)cookie, "px");
}
int main( int argc, const char* argv[] )
{
// get the input file from first argument or use default
const char* input = argc > 1 ? argv[1] : "styles.scss";
// create the file context and get all related structs
struct Sass_File_Context* file_ctx = sass_make_file_context(input);
struct Sass_Context* ctx = sass_file_context_get_context(file_ctx);
struct Sass_Options* ctx_opt = sass_context_get_options(ctx);
// allocate a custom function caller
Sass_Function_Entry fn_foo =
sass_make_function("foo()", call_fn_foo, (void*)42);
// create list of all custom functions
Sass_Function_List fn_list = sass_make_function_list(1);
sass_function_set_list_entry(fn_list, 0, fn_foo);
sass_option_set_c_functions(ctx_opt, fn_list);
// context is set up, call the compile step now
int status = sass_compile_file_context(file_ctx);
// print the result or the error to the stdout
if (status == 0) puts(sass_context_get_output_string(ctx));
else puts(sass_context_get_error_message(ctx));
// release allocated memory
sass_delete_file_context(file_ctx);
// exit status
return status;
}
```
### Compile main.c
```bash
gcc -c main.c -o main.o
gcc -o sample main.o -lsass
echo "foo { margin: foo(); }" > foo.scss
./sample foo.scss => "foo { margin: 42px }"
```
```C
// Struct to hold custom function callback
struct Sass_Function {
const char* signature;
Sass_Function_Fn function;
void* cookie;
};
```
Sass functions are used to define new custom functions callable by Sass code. They are also used to overload debug or error statements. You can also define a fallback function, which is called for every unknown function found in the Sass code. Functions get passed zero or more `Sass_Values` (a `Sass_List` value) and they must also return a `Sass_Value`. Return a `Sass_Error` if you want to signal an error.
## Special signatures
- `*` - Fallback implementation
- `@warn` - Overload warn statements
- `@error` - Overload error statements
- `@debug` - Overload debug statements
Note: The fallback implementation will be given the name of the called function as the first argument, before all the original function arguments. These features are pretty new and should be considered experimental.
### Basic Usage
```C
#include "sass/functions.h"
```
## Sass Function API
```C
// Forward declaration
struct Sass_Compiler;
struct Sass_Function;
// Typedef helpers for custom functions lists
typedef struct Sass_Function (*Sass_Function_Entry);
typedef struct Sass_Function* (*Sass_Function_List);
// Typedef defining function signature and return type
typedef union Sass_Value* (*Sass_Function_Fn)
(const union Sass_Value*, Sass_Function_Entry cb, struct Sass_Compiler* compiler);
// Creators for sass function list and function descriptors
Sass_Function_List sass_make_function_list (size_t length);
Sass_Function_Entry sass_make_function (const char* signature, Sass_Function_Fn cb, void* cookie);
// In case you need to free them yourself
void sass_delete_function (Sass_Function_Entry entry);
void sass_delete_function_list (Sass_Function_List list);
// Setters and getters for callbacks on function lists
Sass_Function_Entry sass_function_get_list_entry(Sass_Function_List list, size_t pos);
void sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb);
// Setters to insert an entry into the import list (you may also use [] access directly)
// Since we are dealing with pointers they should have a guaranteed and fixed size
void sass_import_set_list_entry (Sass_Import_List list, size_t idx, Sass_Import_Entry entry);
Sass_Import_Entry sass_import_get_list_entry (Sass_Import_List list, size_t idx);
// Getters for custom function descriptors
const char* sass_function_get_signature (Sass_Function_Entry cb);
Sass_Function_Fn sass_function_get_function (Sass_Function_Entry cb);
void* sass_function_get_cookie (Sass_Function_Entry cb);
// Getters for callee entry
const char* sass_callee_get_name (Sass_Callee_Entry);
const char* sass_callee_get_path (Sass_Callee_Entry);
size_t sass_callee_get_line (Sass_Callee_Entry);
size_t sass_callee_get_column (Sass_Callee_Entry);
enum Sass_Callee_Type sass_callee_get_type (Sass_Callee_Entry);
Sass_Env_Frame sass_callee_get_env (Sass_Callee_Entry);
// Getters and Setters for environments (lexical, local and global)
union Sass_Value* sass_env_get_lexical (Sass_Env_Frame, const char*);
void sass_env_set_lexical (Sass_Env_Frame, const char*, union Sass_Value*);
union Sass_Value* sass_env_get_local (Sass_Env_Frame, const char*);
void sass_env_set_local (Sass_Env_Frame, const char*, union Sass_Value*);
union Sass_Value* sass_env_get_global (Sass_Env_Frame, const char*);
void sass_env_set_global (Sass_Env_Frame, const char*, union Sass_Value*);
```
### More links
- [Sass Function Example](api-function-example.md)
- [Sass Function Internal](api-function-internal.md)
## Example importer.c
```C
#include <stdio.h>
#include <string.h>
#include "sass/context.h"
Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp)
{
// get the cookie from importer descriptor
void* cookie = sass_importer_get_cookie(cb);
Sass_Import_List list = sass_make_import_list(2);
char* local = sass_copy_c_string("local { color: green; }");
char* remote = sass_copy_c_string("remote { color: red; }");
list[0] = sass_make_import_entry("/tmp/styles.scss", local, 0);
list[1] = sass_make_import_entry("http://www.example.com", remote, 0);
return list;
}
int main( int argc, const char* argv[] )
{
// get the input file from first argument or use default
const char* input = argc > 1 ? argv[1] : "styles.scss";
// create the file context and get all related structs
struct Sass_File_Context* file_ctx = sass_make_file_context(input);
struct Sass_Context* ctx = sass_file_context_get_context(file_ctx);
struct Sass_Options* ctx_opt = sass_context_get_options(ctx);
// allocate custom importer
Sass_Importer_Entry c_imp =
sass_make_importer(sass_importer, 0, 0);
// create list for all custom importers
Sass_Importer_List imp_list = sass_make_importer_list(1);
// put only the importer on to the list
sass_importer_set_list_entry(imp_list, 0, c_imp);
// register list on to the context options
sass_option_set_c_importers(ctx_opt, imp_list);
// context is set up, call the compile step now
int status = sass_compile_file_context(file_ctx);
// print the result or the error to the stdout
if (status == 0) puts(sass_context_get_output_string(ctx));
else puts(sass_context_get_error_message(ctx));
// release allocated memory
sass_delete_file_context(file_ctx);
// exit status
return status;
}
```
Compile importer.c
```bash
gcc -c importer.c -o importer.o
gcc -o importer importer.o -lsass
echo "@import 'foobar';" > importer.scss
./importer importer.scss
```
## Importer Behavior Examples
```C
Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) {
// let LibSass handle the import request
return NULL;
}
Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) {
// let LibSass handle the request
// swallows »@import "http://…"« pass-through
// (arguably a bug)
Sass_Import_List list = sass_make_import_list(1);
list[0] = sass_make_import_entry(path, 0, 0);
return list;
}
Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) {
// return an error to halt execution
Sass_Import_List list = sass_make_import_list(1);
const char* message = "some error message";
list[0] = sass_make_import_entry(path, 0, 0);
sass_import_set_error(list[0], sass_copy_c_string(message), 0, 0);
return list;
}
Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) {
// let LibSass load the file identifed by the importer
Sass_Import_List list = sass_make_import_list(1);
list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0);
return list;
}
Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) {
// completely hide the import
// (arguably a bug)
Sass_Import_List list = sass_make_import_list(0);
return list;
}
Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) {
// completely hide the import
// (arguably a bug)
Sass_Import_List list = sass_make_import_list(1);
list[0] = sass_make_import_entry(0, 0, 0);
return list;
}
```
```C
// External import entry
struct Sass_Import {
char* imp_path; // path as found in the import statement
char *abs_path; // path after importer has resolved it
char* source;
char* srcmap;
// error handling
char* error;
size_t line;
size_t column;
};
// Struct to hold importer callback
struct Sass_Importer {
Sass_Importer_Fn importer;
double priority;
void* cookie;
};
```
By using custom importers, Sass stylesheets can be implemented in any possible way, such as by being loaded via a remote server. Please note: this feature is experimental and is implemented differently than importers in Ruby Sass. Imports must be relative to the parent import context and therefore we need to pass this information to the importer callback. This is currently done by passing the complete import string/path of the previous import context.
## Return Imports
You actually have to return a list of imports, since some importers may want to import multiple files from one import statement (ie. a glob/star importer). The memory you pass with source and srcmap is taken over by LibSass and freed automatically when the import is done. You are also allowed to return `0` instead of a list, which will tell LibSass to handle the import by itself (as if no custom importer was in use).
```C
Sass_Import_Entry* rv = sass_make_import_list(1);
rv[0] = sass_make_import(rel, abs, source, srcmap);
```
Every import will then be included in LibSass. You are allowed to only return a file path without any loaded source. This way you can ie. implement rewrite rules for import paths and leave the loading part for LibSass.
Please note that LibSass doesn't use the srcmap parameter yet. It has been added to not deprecate the C-API once support has been implemented. It will be used to re-map the actual sourcemap with the provided ones.
### Basic Usage
```C
#include "sass/functions.h"
```
## Sass Importer API
```C
// Forward declaration
struct Sass_Import;
// Forward declaration
struct Sass_C_Import_Descriptor;
// Typedef defining the custom importer callback
typedef struct Sass_C_Import_Descriptor (*Sass_C_Import_Callback);
// Typedef defining the importer c function prototype
typedef Sass_Import_Entry* (*Sass_C_Import_Fn) (const char* url, const char* prev, void* cookie);
// Creators for custom importer callback (with some additional pointer)
// The pointer is mostly used to store the callback into the actual function
Sass_C_Import_Callback sass_make_importer (Sass_C_Import_Fn, void* cookie);
// Getters for import function descriptors
Sass_C_Import_Fn sass_import_get_function (Sass_C_Import_Callback fn);
void* sass_import_get_cookie (Sass_C_Import_Callback fn);
// Deallocator for associated memory
void sass_delete_importer (Sass_C_Import_Callback fn);
// Creator for sass custom importer return argument list
Sass_Import_Entry* sass_make_import_list (size_t length);
// Creator for a single import entry returned by the custom importer inside the list
Sass_Import_Entry sass_make_import_entry (const char* path, char* source, char* srcmap);
Sass_Import_Entry sass_make_import (const char* rel, const char* abs, char* source, char* srcmap);
// set error message to abort import and to print out a message (path from existing object is used in output)
Sass_Import_Entry sass_import_set_error(Sass_Import_Entry import, const char* message, size_t line, size_t col);
// Setters to insert an entry into the import list (you may also use [] access directly)
// Since we are dealing with pointers they should have a guaranteed and fixed size
void sass_import_set_list_entry (Sass_Import_Entry* list, size_t idx, Sass_Import_Entry entry);
Sass_Import_Entry sass_import_get_list_entry (Sass_Import_Entry* list, size_t idx);
// Getters for import entry
const char* sass_import_get_imp_path (Sass_Import_Entry);
const char* sass_import_get_abs_path (Sass_Import_Entry);
const char* sass_import_get_source (Sass_Import_Entry);
const char* sass_import_get_srcmap (Sass_Import_Entry);
// Explicit functions to take ownership of these items
// The property on our struct will be reset to NULL
char* sass_import_take_source (Sass_Import_Entry);
char* sass_import_take_srcmap (Sass_Import_Entry);
// Getters for import error entries
size_t sass_import_get_error_line (Sass_Import_Entry);
size_t sass_import_get_error_column (Sass_Import_Entry);
const char* sass_import_get_error_message (Sass_Import_Entry);
// Deallocator for associated memory (incl. entries)
void sass_delete_import_list (Sass_Import_Entry*);
// Just in case we have some stray import structs
void sass_delete_import (Sass_Import_Entry);
```
### More links
- [Sass Importer Example](api-importer-example.md)
- [Sass Importer Internal](api-importer-internal.md)
## Example operation.c
```C
#include <stdio.h>
#include <string.h>
#include "sass/values.h"
int main( int argc, const char* argv[] )
{
// create two new sass values to be added
union Sass_Value* string = sass_make_string("String");
union Sass_Value* number = sass_make_number(42, "nits");
// invoke the add operation which returns a new sass value
union Sass_Value* total = sass_value_op(ADD, string, number);
// no further use for the two operands
sass_delete_value(string);
sass_delete_value(number);
// this works since libsass will always return a
// string for add operations with a string as the
// left hand side. But you should never rely on it!
puts(sass_string_get_value(total));
// invoke stringification (uncompressed with precision of 5)
union Sass_Value* result = sass_value_stringify(total, false, 5);
// no further use for the sum
sass_delete_value(total);
// print the result - you may want to make
// sure result is indeed a string, altough
// stringify guarantees to return a string
// if (sass_value_is_string(result)) {}
// really depends on your level of paranoia
puts(sass_string_get_value(result));
// finally free result
sass_delete_value(result);
// exit status
return 0;
}
```
## Compile operation.c
```bash
gcc -c operation.c -o operation.o
gcc -o operation operation.o -lsass
./operation # => String42nits
```
```C
struct Sass_Unknown {
enum Sass_Tag tag;
};
struct Sass_Boolean {
enum Sass_Tag tag;
bool value;
};
struct Sass_Number {
enum Sass_Tag tag;
double value;
char* unit;
};
struct Sass_Color {
enum Sass_Tag tag;
double r;
double g;
double b;
double a;
};
struct Sass_String {
enum Sass_Tag tag;
char* value;
};
struct Sass_List {
enum Sass_Tag tag;
enum Sass_Separator separator;
size_t length;
// null terminated "array"
union Sass_Value** values;
};
struct Sass_Map {
enum Sass_Tag tag;
size_t length;
struct Sass_MapPair* pairs;
};
struct Sass_Null {
enum Sass_Tag tag;
};
struct Sass_Error {
enum Sass_Tag tag;
char* message;
};
struct Sass_Warning {
enum Sass_Tag tag;
char* message;
};
union Sass_Value {
struct Sass_Unknown unknown;
struct Sass_Boolean boolean;
struct Sass_Number number;
struct Sass_Color color;
struct Sass_String string;
struct Sass_List list;
struct Sass_Map map;
struct Sass_Null null;
struct Sass_Error error;
struct Sass_Warning warning;
};
struct Sass_MapPair {
union Sass_Value* key;
union Sass_Value* value;
};
```
`Sass_Values` are used to pass values and their types between the implementer
and LibSass. Sass knows various different value types (including nested arrays
and hash-maps). If you implement a binding to another programming language, you
have to find a way to [marshal][1] (convert) `Sass_Values` between the target
language and C. `Sass_Values` are currently only used by custom functions, but
it should also be possible to use them without a compiler context.
[1]: https://en.wikipedia.org/wiki/Marshalling_%28computer_science%29
### Basic Usage
```C
#include "sass/values.h"
```
```C
// Type for Sass values
enum Sass_Tag {
SASS_BOOLEAN,
SASS_NUMBER,
SASS_COLOR,
SASS_STRING,
SASS_LIST,
SASS_MAP,
SASS_NULL,
SASS_ERROR,
SASS_WARNING
};
// Tags for denoting Sass list separators
enum Sass_Separator {
SASS_COMMA,
SASS_SPACE,
// only used internally to represent a hash map before evaluation
// otherwise we would be too early to check for duplicate keys
SASS_HASH
};
// Value Operators
enum Sass_OP {
AND, OR, // logical connectives
EQ, NEQ, GT, GTE, LT, LTE, // arithmetic relations
ADD, SUB, MUL, DIV, MOD, // arithmetic functions
NUM_OPS // so we know how big to make the op table
};
```
### Sass Value API
```C
// Forward declaration
union Sass_Value;
// Creator functions for all value types
union Sass_Value* sass_make_null (void);
union Sass_Value* sass_make_boolean (bool val);
union Sass_Value* sass_make_string (const char* val);
union Sass_Value* sass_make_qstring (const char* val);
union Sass_Value* sass_make_number (double val, const char* unit);
union Sass_Value* sass_make_color (double r, double g, double b, double a);
union Sass_Value* sass_make_list (size_t len, enum Sass_Separator sep, bool is_bracketed);
union Sass_Value* sass_make_map (size_t len);
union Sass_Value* sass_make_error (const char* msg);
union Sass_Value* sass_make_warning (const char* msg);
// Generic destructor function for all types
// Will release memory of all associated Sass_Values
// Means we will delete recursively for lists and maps
void sass_delete_value (union Sass_Value* val);
// Make a deep cloned copy of the given sass value
union Sass_Value* sass_clone_value (const union Sass_Value* val);
// Stringify a Sass_Values and also return the result as a Sass_Value (of type STRING)
union Sass_Value* sass_value_stringify (const union Sass_Value* a, bool compressed, int precision);
// Execute an operation for two Sass_Values and return the result as a Sass_Value too
union Sass_Value* sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b);
// Return the sass tag for a generic sass value
// Check is needed before accessing specific values!
enum Sass_Tag sass_value_get_tag (const union Sass_Value* v);
// Check value to be of a specific type
// Can also be used before accessing properties!
bool sass_value_is_null (const union Sass_Value* v);
bool sass_value_is_number (const union Sass_Value* v);
bool sass_value_is_string (const union Sass_Value* v);
bool sass_value_is_boolean (const union Sass_Value* v);
bool sass_value_is_color (const union Sass_Value* v);
bool sass_value_is_list (const union Sass_Value* v);
bool sass_value_is_map (const union Sass_Value* v);
bool sass_value_is_error (const union Sass_Value* v);
bool sass_value_is_warning (const union Sass_Value* v);
// Getters and setters for Sass_Number
double sass_number_get_value (const union Sass_Value* v);
void sass_number_set_value (union Sass_Value* v, double value);
const char* sass_number_get_unit (const union Sass_Value* v);
void sass_number_set_unit (union Sass_Value* v, char* unit);
// Getters and setters for Sass_String
const char* sass_string_get_value (const union Sass_Value* v);
void sass_string_set_value (union Sass_Value* v, char* value);
bool sass_string_is_quoted(const union Sass_Value* v);
void sass_string_set_quoted(union Sass_Value* v, bool quoted);
// Getters and setters for Sass_Boolean
bool sass_boolean_get_value (const union Sass_Value* v);
void sass_boolean_set_value (union Sass_Value* v, bool value);
// Getters and setters for Sass_Color
double sass_color_get_r (const union Sass_Value* v);
void sass_color_set_r (union Sass_Value* v, double r);
double sass_color_get_g (const union Sass_Value* v);
void sass_color_set_g (union Sass_Value* v, double g);
double sass_color_get_b (const union Sass_Value* v);
void sass_color_set_b (union Sass_Value* v, double b);
double sass_color_get_a (const union Sass_Value* v);
void sass_color_set_a (union Sass_Value* v, double a);
// Getter for the number of items in list
size_t sass_list_get_length (const union Sass_Value* v);
// Getters and setters for Sass_List
enum Sass_Separator sass_list_get_separator (const union Sass_Value* v);
void sass_list_set_separator (union Sass_Value* v, enum Sass_Separator value);
bool sass_list_get_is_bracketed (const union Sass_Value* v);
void sass_list_set_is_bracketed (union Sass_Value* v, bool value);
// Getters and setters for Sass_List values
union Sass_Value* sass_list_get_value (const union Sass_Value* v, size_t i);
void sass_list_set_value (union Sass_Value* v, size_t i, union Sass_Value* value);
// Getter for the number of items in map
size_t sass_map_get_length (const union Sass_Value* v);
// Getters and setters for Sass_Map keys and values
union Sass_Value* sass_map_get_key (const union Sass_Value* v, size_t i);
void sass_map_set_key (union Sass_Value* v, size_t i, union Sass_Value*);
union Sass_Value* sass_map_get_value (const union Sass_Value* v, size_t i);
void sass_map_set_value (union Sass_Value* v, size_t i, union Sass_Value*);
// Getters and setters for Sass_Error
char* sass_error_get_message (const union Sass_Value* v);
void sass_error_set_message (union Sass_Value* v, char* msg);
// Getters and setters for Sass_Warning
char* sass_warning_get_message (const union Sass_Value* v);
void sass_warning_set_message (union Sass_Value* v, char* msg);
```
### More links
- [Sass Value Example](api-value-example.md)
- [Sass Value Internal](api-value-internal.md)
To install LibSass, make sure the OS X build tools are installed:
xcode-select --install
## Homebrew
To install homebrew, see [http://brew.sh](http://brew.sh)
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
You can install the latest version of LibSass quite easily with brew.
brew install --HEAD libsass
To update this, do:
brew reinstall --HEAD libsass
Brew will build static and shared libraries, and a `libsass.pc` file in `/usr/local/lib/pkgconfig`.
To use `libsass.pc`, make sure this path is in your `PKG_CONFIG_PATH`
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
## Manually
See the linux instructions [Building-with-autotools](build-with-autotools.md) or [Building-with-makefiles](build-with-makefiles.md)
Here are two ebuilds to compile LibSass and sassc on gentoo linux. If you do not know how to use these ebuilds, you should probably read the gentoo wiki page about [portage overlays](http://wiki.gentoo.org/wiki/Overlay).
## www-misc/libsass/libsass-9999.ebuild
```ebuild
EAPI=4
inherit eutils git-2 autotools
DESCRIPTION="A C/C++ implementation of a Sass compiler."
HOMEPAGE="http://libsass.org/"
EGIT_PROJECT='libsass'
EGIT_REPO_URI="https://github.com/sass/libsass.git"
LICENSE="MIT"
SLOT="0"
KEYWORDS=""
IUSE=""
DEPEND=""
RDEPEND="${DEPEND}"
DEPEND="${DEPEND}"
pkg_pretend() {
# older gcc is not supported
local major=$(gcc-major-version)
local minor=$(gcc-minor-version)
[[ "${MERGE_TYPE}" != "binary" && ( $major > 4 || ( $major == 4 && $minor &lt; 5 ) ) ]] && \
die "Sorry, but gcc earlier than 4.5 will not work for LibSass."
}
src_prepare() {
eautoreconf
}
```
## www-misc/sassc/sassc-9999.ebuild
```ebuild
EAPI=4
inherit eutils git-2 autotools
DESCRIPTION="Command Line Tool for LibSass."
HOMEPAGE="http://libsass.org/"
EGIT_PROJECT='sassc'
EGIT_REPO_URI="https://github.com/sass/sassc.git"
LICENSE="MIT"
SLOT="0"
KEYWORDS=""
IUSE=""
DEPEND="www-misc/libsass"
RDEPEND="${DEPEND}"
DEPEND="${DEPEND}"
src_prepare() {
eautoreconf
}
```
We support builds via MingGW and via Visual Studio Community 2013.
Both should be considered experimental (MinGW was better tested)!
## Building via MingGW (makefiles)
First grab the latest [MinGW for windows][1] installer. Once it is installed, you can click on continue or open the Installation Manager via `bin\mingw-get.exe`.
You need to have the following components installed:
![Visualization of components installed in the interface](https://cloud.githubusercontent.com/assets/282293/5525466/947bf396-89e6-11e4-841d-4aa916f14de1.png)
Next we need to install [git for windows][2]. You probably want to check the option to add it to the global path, but you do not need to install the unix tools.
If you want to run the spec test-suite you also need [ruby][3] and a few gems available. Grab the [latest installer][3] and make sure to add it the global path. Then install the missing gems:
```bash
gem install minitest
```
### Mount the mingw root directory
As mentioned in the [MinGW Getting Started](http://www.mingw.org/wiki/Getting_Started#toc5) guide, you should edit `C:\MinGW\msys\1.0\etc\fstab` to contain the following line:
```
C:\MinGW /mingw
```
### Starting a "MingGW" console
Create a batch file with this content:
```bat
@echo off
set PATH=C:\MinGW\bin;%PATH%
REM only needed if not already available
set PATH=%PROGRAMFILES%\git\bin;%PATH%
REM C:\MinGW\msys\1.0\msys.bat
cmd
```
Execute it and make sure these commands can be called: `git`, `mingw32-make`, `rm` and `gcc`! Once this is all set, you should be ready to compile `libsass`!
### Get the sources
```bash
# using git is preferred
git clone https://github.com/sass/libsass.git
# only needed for sassc and/or testsuite
git clone https://github.com/sass/sassc.git libsass/sassc
git clone https://github.com/sass/sass-spec.git libsass/sass-spec
```
### Decide for static or shared library
`libsass` can be built and linked as a `static` or as a `shared` library. The default is `static`. To change it you can set the `BUILD` environment variable:
```bat
set BUILD="shared"
```
### Compile the library
```bash
mingw32-make -C libsass
```
### Results can be found in
```bash
$ ls libsass/lib
libsass.a libsass.dll libsass.so
```
### Run the spec test-suite
```bash
mingw32-make -C libsass test_build
```
## Building via MingGW 64bit (makefiles)
Building libass to dll on window 64bit.
+ downloads [MinGW64 for windows7 64bit](http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v3-rev0.7z/download) , and unzip to "C:\mingw64".
+ Create a batch file with this content:
```bat
@echo off
set PATH=C:\mingw64\bin;%PATH%
set CC=gcc
REM only needed if not already available
set PATH=%PROGRAMFILES%\Git\bin;%PATH%
REM C:\MinGW\msys\1.0\msys.bat
cmd
```
+ By default , mingw64 dll will depends on "​m​i​n​g​w​m​1​0​.​d​l​l​、​ ​l​i​b​g​c​c​_​s​_​d​w​2​-​1​.​d​l​l​" , we can modify Makefile to fix this:(add "-static")
``` bash
lib/libsass.dll: $(COBJECTS) $(OBJECTS) $(RCOBJECTS)
$(MKDIR) lib
$(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) -s -static -Wl,--subsystem,windows,--out-implib,lib/libsass.a
```
+ Compile the library
```bash
mingw32-make -C libsass
```
By the way , if you are using java jna , [JNAerator](http://jnaerator.googlecode.com/) is a good tool.
## Building via Visual Studio Community 2013
Open a Visual Studio 2013 command prompt:
- `VS2013 x86 Native Tools Command Prompt`
Note: When I installed the community edition, I only got the 2012 command prompts. I copied them from the Startmenu to the Desktop and adjusted the paths from `Visual Studio 11.0` to `Visual Studio 12.0`. Since `libsass` uses some `C++11` features, you need at least a MSVC 2013 compiler (v120).
### Get the source
```bash
# using git is preferred
git clone https://github.com/sass/libsass.git
git clone https://github.com/sass/sassc.git libsass/sassc
# only needed if you want to run the testsuite
git clone https://github.com/sass/sass-spec.git libsass/sass-spec
```
### Compile sassc
Sometimes `msbuild` seems not available from the command prompt. Just search for it and add it to the global path. It seems to be included in the .net folders too.
```bat
cd libsass
REM set PATH=%PATH%;%PROGRAMFILES%\MSBuild\12.0\Bin
msbuild /m:4 /p:Configuration=Release win\libsass.sln
REM running the spec test-suite manually (needs ruby and minitest gem)
ruby sass-spec\sass-spec.rb -V 3.5 -c win\bin\sassc.exe -s --impl libsass sass-spec/spec
cd ..
```
[1]: http://sourceforge.net/projects/mingw/files/latest/download?source=files
[2]: https://msysgit.github.io/
[3]: http://rubyinstaller.org/
This page is mostly intended for people that want to build a system library that gets distributed via RPMs or other means. This is currently in a experimental phase, as we currently do not really guarantee any ABI forward compatibility. The C API was rewritten to make this possible in the future, but we want to wait some more time till we can call this final and stable.
Building via autotools
--
You want to build a system library only via autotools, since it will create the proper `libtool` files to make it loadable on multiple systems. We hope this works correctly, but nobody of the `libsass` core team has much knowledge in this area. Therefore we are open for comments or improvements by people that have more experience in that matter (like package maintainers from various linux distributions).
```bash
apt-get install autoconf libtool
git clone https://github.com/sass/libsass.git
cd libsass
autoreconf --force --install
./configure \
--disable-tests \
--disable-static \
--enable-shared \
--prefix=/usr
make -j5 install
cd ..
```
This should install these files
```bash
# $ ls -la /usr/lib/libsass.*
/usr/lib/libsass.la
/usr/lib/libsass.so -> libsass.so.0.0.9
/usr/lib/libsass.so.0 -> libsass.so.0.0.9
/usr/lib/libsass.so.0.0.9
# $ ls -la /usr/include/sass*
/usr/include/sass.h
/usr/include/sass2scss.h
/usr/include/sass/context.h
/usr/include/sass/functions.h
/usr/include/sass/values.h
```
### Get the sources
```bash
# using git is preferred
git clone https://github.com/sass/libsass.git
# only needed for sassc and/or testsuite
git clone https://github.com/sass/sassc.git libsass/sassc
git clone https://github.com/sass/sass-spec.git libsass/sass-spec
```
### Prerequisites
In order to run autotools you need a few tools installed on your system.
```bash
yum install automake libtool # RedHat Linux
emerge -a automake libtool # Gentoo Linux
pkgin install automake libtool # SmartOS
```
### Create configure script
```bash
cd libsass
autoreconf --force --install
cd ..
```
### Create custom makefiles
```bash
cd libsass
./configure \
--disable-tests \
--disable-shared \
--prefix=/usr
cd ..
```
### Build the library
```bash
make -C libsass -j5
```
### Install the library
The library will be installed to the location given as `prefix` to `configure`. This is standard behavior for autotools and not `libsass` specific.
```bash
make -C libsass -j5 install
```
### Configure options
The `configure` script is created by autotools. To get an overview of available options you can call `./configure --help`. When you execute this script, it will create specific makefiles, which you then use via the regular make command.
There are some `libsass` specific options:
```
Optional Features:
--enable-tests enable testing the build
--enable-coverage enable coverage report for test suite
--enable-shared build shared libraries [default=yes]
--enable-static build static libraries [default=yes]
Optional Packages:
--with-sassc-dir=<dir> specify directory of sassc sources for
testing (default: sassc)
--with-sass-spec-dir=<dir> specify directory of sass-spec for testing
(default: sass-spec)
```
### Build sassc and run spec test-suite
```bash
cd libsass
autoreconf --force --install
./configure \
--enable-tests \
--enable-shared \
--prefix=/usr
make -j5 test_build
cd ..
```
### Get the sources
```bash
# using git is preferred
git clone https://github.com/sass/libsass.git
# only needed for sassc and/or testsuite
git clone https://github.com/sass/sassc.git libsass/sassc
git clone https://github.com/sass/sass-spec.git libsass/sass-spec
```
### Decide for static or shared library
`libsass` can be built and linked as a `static` or as a `shared` library. The default is `static`. To change it you can set the `BUILD` environment variable:
```bash
export BUILD="shared"
```
Alternatively you can also define it directly when calling make:
```bash
BUILD="shared" make ...
```
### Compile the library
```bash
make -C libsass -j5
```
### Results can be found in
```bash
$ ls libsass/lib
libsass.a libsass.so
```
### Install onto the system
We recommend to use [autotools to install](build-with-autotools.md) libsass onto the
system, since that brings all the benefits of using libtools as the main install method.
If you still want to install libsass via the makefile, you need to make sure that gnu
`install` utility (or compatible) is installed on your system.
```bash
yum install coreutils # RedHat Linux
emerge -a coreutils # Gentoo Linux
pkgin install coreutils # SmartOS
```
You can set the install location by setting `PREFIX`
```bash
PREFIX="/opt/local" make install
```
### Compling sassc
```bash
# Let build know library location
export SASS_LIBSASS_PATH="`pwd`/libsass"
# Invokes the sassc makefile
make -C libsass -j5 sassc
```
### Run the spec test-suite
```bash
# needs ruby available
# also gem install minitest
make -C libsass -j5 test_build
```
## Building LibSass with MingGW (makefiles)
First grab the latest [MinGW for windows][1] installer. Once it is installed, you can click on continue or open the Installation Manager via `bin\mingw-get.exe`.
You need to have the following components installed:
![](https://cloud.githubusercontent.com/assets/282293/5525466/947bf396-89e6-11e4-841d-4aa916f14de1.png)
Next we need to install [git for windows][2]. You probably want to check the option to add it to the global path, but you do not need to install the unix tools.
If you want to run the spec test-suite you also need [ruby][3] and a few gems available. Grab the [latest installer][3] and make sure to add it the global path. Then install the missing gems:
```bash
gem install minitest
```
### Mount the mingw root directory
As mentioned in the [MinGW Getting Started](http://www.mingw.org/wiki/Getting_Started#toc5) guide, you should edit `C:\MinGW\msys\1.0\etc\fstab` to contain the following line:
```
C:\MinGW /mingw
```
### Starting a "MingGW" console
Create a batch file with this content:
```bat
@echo off
set PATH=C:\MinGW\bin;%PATH%
REM only needed if not already available
set PATH=%PROGRAMFILES%\git\bin;%PATH%
REM C:\MinGW\msys\1.0\msys.bat
cmd
```
Execute it and make sure these commands can be called: `git`, `mingw32-make`, `rm` and `gcc`! Once this is all set, you should be ready to compile `libsass`!
### Get the sources
```bash
# using git is preferred
git clone https://github.com/sass/libsass.git
# only needed for sassc and/or testsuite
git clone https://github.com/sass/sassc.git libsass/sassc
git clone https://github.com/sass/sass-spec.git libsass/sass-spec
```
### Decide for static or shared library
`libsass` can be built and linked as a `static` or as a `shared` library. The default is `static`. To change it you can set the `BUILD` environment variable:
```bat
set BUILD="shared"
```
### Compile the library
```bash
mingw32-make -C libsass
```
### Results can be found in
```bash
$ ls libsass/lib
libsass.a libsass.dll libsass.so
```
### Run the spec test-suite
```bash
mingw32-make -C libsass test_build
```
## Building via MingGW 64bit (makefiles)
Building libass to dll on window 64bit.
Download [MinGW64 for windows7 64bit](http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v3-rev0.7z/download) and unzip to "C:\mingw64".
Create a batch file with this content:
```bat
@echo off
set PATH=C:\mingw64\bin;%PATH%
set CC=gcc
REM only needed if not already available
set PATH=%PROGRAMFILES%\Git\bin;%PATH%
REM C:\MinGW\msys\1.0\msys.bat
cmd
```
By default, mingw64 dll will depends on "​m​i​n​g​w​m​1​0​.​d​l​l​、​ ​l​i​b​g​c​c​_​s​_​d​w​2​-​1​.​d​l​l​", we can modify Makefile to fix this:(add "-static")
``` bash
lib/libsass.dll: $(COBJECTS) $(OBJECTS) $(RCOBJECTS)
$(MKDIR) lib
$(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) -s -static -Wl,--subsystem,windows,--out-implib,lib/libsass.a
```
Compile the library
```bash
mingw32-make -C libsass
```
By the way, if you are using java jna, [JNAerator](http://jnaerator.googlecode.com/) is a good tool.
[1]: http://sourceforge.net/projects/mingw/files/latest/download?source=files
[2]: https://msysgit.github.io/
[3]: http://rubyinstaller.org/
## Building LibSass with Visual Studio
### Requirements:
The minimum requirement to build LibSass with Visual Studio is "Visual Studio 2013 Express for Desktop".
Additionally, it is recommended to have `git` installed and available in `PATH`, so to deduce the `libsass` version information. For instance, if GitHub for Windows (https://windows.github.com/) is installed, the `PATH` will have an entry resembling: `X:\Users\<YOUR_NAME>\AppData\Local\GitHub\PortableGit_<SOME_GUID>\cmd\` (where `X` is the drive letter of system drive). If `git` is not available, inquiring the LibSass version will result in `[NA]`.
### Build Steps:
#### From Visual Studio:
On opening the `win\libsass.sln` solution and build (Ctrl+Shift+B) to build `libsass.dll`.
To Build LibSass as a static Library, it is recommended to set an environment variable `LIBSASS_STATIC_LIB` before launching the project:
```cmd
cd path\to\libsass
SET LIBSASS_STATIC_LIB=1
::
:: or in PowerShell:
:: $env:LIBSASS_STATIC_LIB=1
::
win\libsass.sln
```
Visual Studio will form the filtered source tree as shown below:
![image](https://cloud.githubusercontent.com/assets/3840695/9298985/aae9e072-44bf-11e5-89eb-e7995c098085.png)
`Header Files` contains the .h and .hpp files, while `Source Files` covers `.c` and `.cpp`. The other used headers/sources will appear under `External Dependencies`.
If there is a LibSass code file appearing under External Dependencies, it can be changed by altering the `win\libsass.vcxproj.filters` file or dragging in Solution Explorer.
#### From Command Prompt:
Notice that in the following commands:
* If the platform is 32-bit Windows, replace `ProgramFiles(x86)` with `ProgramFiles`.
* To build with Visual Studio 2015, replace `12.0` with `14.0` in the aforementioned command.
Open a command prompt:
To build dynamic/shared library (`libsass.dll`):
```cmd
:: debug build:
"%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln
:: release build:
"%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ^
/p:Configuration=Release
```
To build static library (`libsass.lib`):
```cmd
:: debug build:
"%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ^
/p:LIBSASS_STATIC_LIB=1
:: release build:
"%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ^
/p:LIBSASS_STATIC_LIB=1 /p:Configuration=Release
```
#### From PowerShell:
To build dynamic/shared library (`libsass.dll`):
```powershell
# debug build:
&"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln
# release build:
&"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln `
/p:Configuration=Release
```
To build static library (`libsass.lib`):
```powershell
# build:
&"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln `
/p:LIBSASS_STATIC_LIB=1
# release build:
&"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln `
/p:LIBSASS_STATIC_LIB=1 /p:Configuration=Release
```
`libsass` is only a library and does not do much on its own. You need an implementation that you can use from the [command line][6]. Or some [bindings|Implementations][9] to use it within your favorite programming language. You should be able to get [`sassc`][6] running by following the instructions in this guide.
Before starting, see [setup dev environment](setup-environment.md).
Building on different Operating Systems
--
We try to keep the code as OS independent and standard compliant as possible. Reading files from the file-system has some OS depending code, but will ultimately fall back to a posix compatible implementation. We do use some `C++11` features, but are so far only committed to use `unordered_map`. This means you will need a pretty recent compiler on most systems (gcc 4.5 seems to be the minimum).
### Building on Linux (and other *nix flavors)
Linux is the main target for `libsass` and we support two ways to build `libsass` here. The old plain makefiles should still work on most systems (including MinGW), while the autotools build is preferred if you want to create a [system library] (experimental).
- [Building with makefiles][1]
- [Building with autotools][2]
### Building on Windows (experimental)
Windows build support was added very recently and should be considered experimental. Credits go to @darrenkopp and @am11 for their work on getting `libsass` and `sassc` to compile with visual studio!
- [Building with MinGW][3]
- [Building with Visual Studio][11]
### Building on Max OS X (untested)
Works the same as on linux, but you can also install LibSass via `homebrew`.
- [Building on Mac OS X][10]
### Building a system library (experimental)
Since `libsass` is a library, it makes sense to install it as a shared library on your system. On linux this means creating a `.so` library via autotools. This should work pretty well already, but we are not yet committed to keep the ABI 100% stable. This should be the case once we increase the version number for the library to 1.0.0 or higher. On Windows you should be able get a `dll` by creating a shared build with MinGW. There is currently no target in the MSVC project files to do this.
- [Building shared system library][4]
Compiling with clang instead of gcc
--
To use clang you just need to set the appropriate environment variables:
```bash
export CC=/usr/bin/clang
export CXX=/usr/bin/clang++
```
Running the spec test-suite
--
We constantly and automatically test `libsass` against the official [spec test-suite][5]. To do this we need to have a test-runner (which is written in ruby) and a command-line tool ([`sassc`][6]) to run the tests. Therefore we need to additionally compile `sassc`. To do this, the build files of all three projects need to work together. This may not have the same quality for all build flavors. You definitely need to have ruby (2.1?) installed (version 1.9 seems to cause problems at least on windows). You also need some gems installed:
```bash
ruby -v
gem install minitest
# should be optional
gem install minitap
```
Including the LibSass version
--
There is a function in `libsass` to query the current version. This has to be defined at compile time. We use a C macro for this, which can be defined by calling `g++ -DLIBSASS_VERSION="\"x.y.z.\""`. The two quotes are necessary, since it needs to end up as a valid C string. Normally you do not need to do anything if you use the makefiles or autotools. They will try to fetch the version via git directly. If you only have the sources without the git repo, you can pass the version as an environment variable to `make` or `configure`:
```
export LIBSASS_VERSION="x.y.z."
```
Continuous Integration
--
We use two CI services to automatically test all commits against the latest [spec test-suite][5].
- [LibSass on Travis-CI (linux)][7]
[![Build Status](https://travis-ci.org/sass/libsass.png?branch=master)](https://travis-ci.org/sass/libsass)
- [LibSass on AppVeyor (windows)][8]
[![Build status](https://ci.appveyor.com/api/projects/status/github/sass/libsass?svg=true)](https://ci.appveyor.com/project/mgreter/libsass-513/branch/master)
Why not using CMake?
--
There were some efforts to get `libsass` to compile with CMake, which should make it easier to create build files for linux and windows. Unfortunately this was not completed. But we are certainly open for PRs!
Miscellaneous
--
- [Ebuilds for Gentoo Linux](build-on-gentoo.md)
[1]: build-with-makefiles.md
[2]: build-with-autotools.md
[3]: build-with-mingw.md
[4]: build-shared-library.md
[5]: https://github.com/sass/sass-spec
[6]: https://github.com/sass/sassc
[7]: https://github.com/sass/libsass/blob/master/.travis.yml
[8]: https://github.com/sass/libsass/blob/master/appveyor.yml
[9]: implementations.md
[10]: build-on-darwin.md
[11]: build-with-visual-studio.md
This document is to serve as a living, changing plan for getting LibSass caught up with Ruby Sass.
_Note: an "s" preceeding a version number is specifying a Ruby Sass version. Without an s, it's a version of LibSass._
# Goal
**Our goal is to reach full s3.4 compatibility as soon as possible. LibSass version 3.4 will behave just like Ruby Sass 3.4**
I highlight the goal, because there are some things that are *not* currently priorities. To be clear, they WILL be priorities, but they are not at the moment:
* Performance Improvements
* Extensibility
The overriding goal is correctness.
## Verifying Correctness
LibSass uses the spec for its testing. The spec was originally based off s3.2 tests. Many things have changed in Ruby Sass since then and some of the tests need to be updated and changed in order to get them to match both LibSass and Ruby Sass.
Until this project is complete, the spec will be primarily a place to test LibSass. By the time LibSass reaches 3.4, it is our goal that sass-spec will be fully usable as an official testing source for ALL implementations of Sass.
## Version Naming
Until LibSass reaches parity with Ruby Sass, we will be aggressively bumping versions, and LibSass 3.4 will be the peer to Ruby Sass 3.4 in every way.
# Release Plan
## 3.0
The goal of 3.0 is to introduce some of the most demanded features for LibSass. That is, we are focusing on issues and features that have kept adoption down. This is a mongrel release wrt which version of Sass it's targeting. It's often a mixture of 3.2 / 3.3 / 3.4 behaviours. This is not ideal, but it's favourable to not existing. Targeting 3.4 strictly during this release would mean we never actually release.
# 3.1
The goal of 3.1 is to update all the passing specs to agree with 3.4. This will not be a complete representation of s3.4 (aka, there will me missing features), but the goal is to change existing features and implemented features to match 3.4 behaviour.
By the end of this, the sass-spec must pass against 3.4.
Major issues:
* Variable Scoping
* Color Handling
* Precision
# 3.2
This version will focus on edge case fixes. There are a LOT of edge cases in the _todo_ tests and this is the release where we hunt those down like dogs (not that we want to hurt dogs, it's just a figure of speech in English).
# 3.3
Dress rehearsal. When we are 99% sure that we've fixed the main issues keeping us from saying we are compliant in s3.4 behaviour.
# 3.4
Compass Compatibility. We need to be able to work with Compass and all the other libraries out there. At this point, we are calling LibSass "mature"
# Beyond 3.4
Obviously, there is matching Sass 3.5 behaviour. But, beyond that, we'll want to focus on performance, stability, and error handling. These can always be improved upon and are the life's work of an open source project. We'll have to work closely with Sass in the future.
......@@ -9,6 +9,7 @@ AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([src/config.h])
AC_CONFIG_FILES([include/sass/version.h])
AC_CONFIG_AUX_DIR([script])
# These are flags passed to automake
# Though they look like gcc flags!
AM_INIT_AUTOMAKE([foreign parallel-tests -Wall])
......@@ -93,21 +94,16 @@ the --with-sass-spec-dir=<dir> argument.
;;
esac
AC_SUBST(SASS_SPEC_PATH)
# TODO: Remove this when automake requirements are 1.12+
AC_MSG_CHECKING([whether we can use TAP mode])
tmp=`$AWK '/TEST_LOG_DRIVER/' $srcdir/GNUmakefile.in`
if test "x$tmp" != "x"; then
use_tap=yes
else
use_tap=no
fi
AC_MSG_RESULT([$use_tap])
else
# we do not really need these paths for non test build
# but automake may error if we do not define them here
SASS_SPEC_PATH=sass-spec
SASS_SASSC_PATH=sassc
AC_SUBST(SASS_SPEC_PATH)
AC_SUBST(SASS_SASSC_PATH)
fi
AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "xyes")
AM_CONDITIONAL(USE_TAP, test "x$use_tap" = "xyes")
AC_ARG_ENABLE([coverage],
[AS_HELP_STRING([--enable-coverage],
......
#ifndef SASS_CONTEXT_H
#define SASS_CONTEXT_H
#include <string>
#include <vector>
#include <map>
#define BUFFERSIZE 255
#include "b64/encode.h"
#include "ast_fwd_decl.hpp"
#include "kwd_arg_macros.hpp"
#include "ast_fwd_decl.hpp"
#include "sass_context.hpp"
#include "environment.hpp"
#include "source_map.hpp"
#include "subset_map.hpp"
#include "backtrace.hpp"
#include "output.hpp"
#include "plugins.hpp"
#include "file.hpp"
struct Sass_Function;
namespace Sass {
class Context {
public:
void import_url (Import_Ptr imp, std::string load_path, const std::string& ctx_path);
bool call_headers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp)
{ return call_loader(load_path, ctx_path, pstate, imp, c_headers, false); };
bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp)
{ return call_loader(load_path, ctx_path, pstate, imp, c_importers, true); };
private:
bool call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp, std::vector<Sass_Importer_Entry> importers, bool only_one = true);
public:
const std::string CWD;
struct Sass_Options& c_options;
std::string entry_path;
size_t head_imports;
Plugins plugins;
Output emitter;
// generic ast node garbage container
// used to avoid possible circular refs
std::vector<AST_Node_Obj> ast_gc;
// resources add under our control
// these are guaranteed to be freed
std::vector<char*> strings;
std::vector<Resource> resources;
std::map<const std::string, StyleSheet> sheets;
Subset_Map subset_map;
std::vector<Sass_Import_Entry> import_stack;
std::vector<Sass_Callee> callee_stack;
std::vector<Backtrace> traces;
struct Sass_Compiler* c_compiler;
// absolute paths to includes
std::vector<std::string> included_files;
// relative includes for sourcemap
std::vector<std::string> srcmap_links;
// vectors above have same size
std::vector<std::string> plugin_paths; // relative paths to load plugins
std::vector<std::string> include_paths; // lookup paths for includes
std::vector<std::string> extensions; // lookup extensions for imports`
void apply_custom_headers(Block_Obj root, const char* path, ParserState pstate);
std::vector<Sass_Importer_Entry> c_headers;
std::vector<Sass_Importer_Entry> c_importers;
std::vector<Sass_Function_Entry> c_functions;
void add_c_header(Sass_Importer_Entry header);
void add_c_importer(Sass_Importer_Entry importer);
void add_c_function(Sass_Function_Entry function);
const std::string indent; // String to be used for indentation
const std::string linefeed; // String to be used for line feeds
const std::string input_path; // for relative paths in src-map
const std::string output_path; // for relative paths to the output
const std::string source_map_file; // path to source map file (enables feature)
const std::string source_map_root; // path for sourceRoot property (pass-through)
virtual ~Context();
Context(struct Sass_Context&);
virtual Block_Obj parse() = 0;
virtual Block_Obj compile();
virtual char* render(Block_Obj root);
virtual char* render_srcmap();
void register_resource(const Include&, const Resource&);
void register_resource(const Include&, const Resource&, ParserState&);
std::vector<Include> find_includes(const Importer& import);
Include load_import(const Importer&, ParserState pstate);
Sass_Output_Style output_style() { return c_options.output_style; };
std::vector<std::string> get_included_files(bool skip = false, size_t headers = 0);
private:
void collect_plugin_paths(const char* paths_str);
void collect_plugin_paths(string_list* paths_array);
void collect_include_paths(const char* paths_str);
void collect_include_paths(string_list* paths_array);
void collect_extensions(const char* extensions_str);
void collect_extensions(string_list* extensions_array);
std::string format_embedded_source_map();
std::string format_source_mapping_url(const std::string& out_path);
// void register_built_in_functions(Env* env);
// void register_function(Signature sig, Native_Function f, Env* env);
// void register_function(Signature sig, Native_Function f, size_t arity, Env* env);
// void register_overload_stub(std::string name, Env* env);
public:
const std::string& cwd() { return CWD; };
};
class File_Context : public Context {
public:
File_Context(struct Sass_File_Context& ctx)
: Context(ctx)
{ }
virtual ~File_Context();
virtual Block_Obj parse();
};
class Data_Context : public Context {
public:
char* source_c_str;
char* srcmap_c_str;
Data_Context(struct Sass_Data_Context& ctx)
: Context(ctx)
{
source_c_str = ctx.source_string;
srcmap_c_str = ctx.srcmap_string;
ctx.source_string = 0; // passed away
ctx.srcmap_string = 0; // passed away
}
virtual ~Data_Context();
virtual Block_Obj parse();
};
}
#endif
First of all, welcome! Thanks for even reading this page. If you're here, you're probably wondering what you can do to help make the LibSass project even more awesome. And, even having that feeling means you are awesome!
## I'm a programmer
Awesome! We need your help. The best thing to do is go find issues that are tagged with both "bug" and "test written". We do spec driven development here and these issues have a test that's written already in the sass-spec project. Go find the test by going to sass-spec/spec/LibSass-todo-issues/issue_XXX/ where XXX is the issue number. Write the code, and compile, and then issue a pull request referencing the issue. We'll quickly verify it and get it merged in!
To get your dev environment setup, check out our article on [Setup-Dev-Environment](setup-environment.md).
## I'm not a backend programmer
COOL! We also need your help. Doing [Issue-Triage](triage.md) is a big deal and something we need constant help with. That means helping to verify issues, write tests for them, and make sure they are getting fixed. It's being part of the smiling face of the project.
Also, we need help with the Sass-Spec project itself. Just people to organize, refactor, and understand the tests in there.
## I don't know what a computer is?
Hmm.... well, it's the thing you are looking at right now. Ummm... check out training courses! Then, come back and join us!
# Developer Documentation
Custom functions are internally represented by `struct Sass_C_Function_Descriptor`.
## Sass_C_Function_Descriptor
```C
struct Sass_C_Function_Descriptor {
const char* signature;
Sass_C_Function function;
void* cookie;
};
```
- `signature`: The function declaration, like `foo($bar, $baz:1)`
- `function`: Reference to the C function callback
- `cookie`: any pointer you want to attach
### signature
The signature defines how the function can be invoked. It also declares which arguments are required and which are optional. Required arguments will be enforced by LibSass and a Sass error is thrown in the event a call as missing an argument. Optional arguments only need to be present when you want to overwrite the default value.
foo($bar, $baz: 2)
In this example, `$bar` is required and will error if not passed. `$baz` is optional and the default value of it is 2. A call like `foo(10)` is therefore equal to `foo(10, 2)`, while `foo()` will produce an error.
### function
The callback function needs to be of the following form:
```C
union Sass_Value* call_sass_function(
const union Sass_Value* s_args,
void* cookie
) {
return sass_clone_value(s_args);
}
```
### cookie
The cookie can hold any pointer you want. In the `perl-libsass` implementation it holds the structure with the reference of the actual registered callback into the perl interpreter. Before that call `perl-libsass` will convert all `Sass_Values` to corresponding perl data types (so they can be used natively inside the perl interpretor). The callback can also return a `Sass_Value`. In `perl-libsass` the actual function returns a perl value, which has to be converted before `libsass` can work with it again!
## Sass_Values
```C
// allocate memory (copies passed strings)
union Sass_Value* sass_make_null (void);
union Sass_Value* sass_make_boolean (bool val);
union Sass_Value* sass_make_string (const char* val);
union Sass_Value* sass_make_qstring (const char* val);
union Sass_Value* sass_make_number (double val, const char* unit);
union Sass_Value* sass_make_color (double r, double g, double b, double a);
union Sass_Value* sass_make_list (size_t len, enum Sass_Separator sep, bool is_bracketed);
union Sass_Value* sass_make_map (size_t len);
union Sass_Value* sass_make_error (const char* msg);
union Sass_Value* sass_make_warning (const char* msg);
// Make a deep cloned copy of the given sass value
union Sass_Value* sass_clone_value (const union Sass_Value* val);
// deallocate memory (incl. all copied memory)
void sass_delete_value (const union Sass_Value* val);
```
## Example main.c
```C
#include <stdio.h>
#include <stdint.h>
#include "sass/context.h"
union Sass_Value* call_fn_foo(const union Sass_Value* s_args, void* cookie)
{
// we actually abuse the void* to store an "int"
return sass_make_number((size_t)cookie, "px");
}
int main( int argc, const char* argv[] )
{
// get the input file from first argument or use default
const char* input = argc > 1 ? argv[1] : "styles.scss";
// create the file context and get all related structs
struct Sass_File_Context* file_ctx = sass_make_file_context(input);
struct Sass_Context* ctx = sass_file_context_get_context(file_ctx);
struct Sass_Options* ctx_opt = sass_context_get_options(ctx);
// allocate a custom function caller
Sass_C_Function_Callback fn_foo =
sass_make_function("foo()", call_fn_foo, (void*)42);
// create list of all custom functions
Sass_C_Function_List fn_list = sass_make_function_list(1);
sass_function_set_list_entry(fn_list, 0, fn_foo);
sass_option_set_c_functions(ctx_opt, fn_list);
// context is set up, call the compile step now
int status = sass_compile_file_context(file_ctx);
// print the result or the error to the stdout
if (status == 0) puts(sass_context_get_output_string(ctx));
else puts(sass_context_get_error_message(ctx));
// release allocated memory
sass_delete_file_context(file_ctx);
// exit status
return status;
}
```
## Compile main.c
```bash
gcc -c main.c -o main.o
gcc -o sample main.o -lsass
echo "foo { margin: foo(); }" > foo.scss
./sample foo.scss => "foo { margin: 42px }"
```
# LibSass smart pointer implementation
LibSass uses smart pointers very similar to `shared_ptr` known
by Boost or C++11. Implementation is a bit less modular since
it was not needed. Various compile time debug options are
available if you need to debug memory life-cycles.
## Memory Classes
### SharedObj
Base class for the actual node implementations. This ensures
that every object has a reference counter and other values.
```c++
class AST_Node : public SharedObj { ... };
```
### SharedPtr (base class for SharedImpl)
Base class that holds on to the pointer. The reference counter
is stored inside the pointer object directly (`SharedObj`).
### SharedImpl (inherits from SharedPtr)
This is the main base class for objects you use in your code. It
will make sure that the memory it points at will be deleted once
all copies to the same object/memory go out of scope.
```c++
Class* pointer = new Class(...);
SharedImpl<Class> obj(pointer);
```
To spare the developer of typing the templated class every time,
we created typedefs for each available AST Node specialization.
```c++
typedef SharedImpl<Number> Number_Obj;
Number_Obj number = SASS_MEMORY_NEW(...);
```
## Memory life-cycles
### Pointer pickups
I often use the terminology of "pickup". This means the moment when
a raw pointer not under any control is assigned to a reference counted
object (`XYZ_Obj = XYZ_Ptr`). From that point on memory will be
automatically released once the object goes out of scope (but only
if the reference counter reaches zero). Main point beeing, you don't
have to worry about memory management yourself.
### Object detach
Sometimes we can't return reference counted objects directly (see
invalid covariant return types problems below). But we often still
need to use reference objects inside a function to avoid leaks when
something throws. For this you can use `detach`, which basically
detaches the pointer memory from the reference counted object. So
when the reference counted object goes out of scope, it will not
free the attached memory. You are now again in charge of freeing
the memory (just assign it to a reference counted object again).
## Circular references
Reference counted memory implementations are prone to circular references.
This can be addressed by using a multi generation garbage collector. But
for our use-case that seems overkill. There is no way so far for users
(sass code) to create circular references. Therefore we can code around
this possible issue. But developers should be aware of this limitation.
There are AFAIR two places where circular references could happen. One is
the `sources` member on every `Selector`. The other one can happen in the
extend code (Node handling). The easy way to avoid this is to only assign
complete object clones to these members. If you know the objects lifetime
is longer than the reference you create, you can also just store the raw
pointer. Once needed this could be solved with weak pointers.
## Addressing the invalid covariant return types problems
If you are not familiar with the mentioned problem, you may want
to read up on covariant return types and virtual functions, i.e.
- http://stackoverflow.com/questions/6924754/return-type-covariance-with-smart-pointers
- http://stackoverflow.com/questions/196733/how-can-i-use-covariant-return-types-with-smart-pointers
- http://stackoverflow.com/questions/2687790/how-to-accomplish-covariant-return-types-when-returning-a-shared-ptr
We hit this issue at least with the CRTP visitor pattern (eval, expand,
listize and so forth). This means we cannot return reference counted
objects directly. We are forced to return raw pointers or we would need
to have a lot of explicit and expensive upcasts by callers/consumers.
### Simple functions that allocate new AST Nodes
In the parser step we often create new objects and can just return a
unique pointer (meaning ownership clearly shifts back to the caller).
The caller/consumer is responsible that the memory is freed.
```c++
typedef Number* Number_Ptr;
int parse_integer() {
... // do the parsing
return 42;
}
Number_Ptr parse_number() {
Number_Ptr p_nr = SASS_MEMORY_NEW(...);
p_nr->value(parse_integer());
return p_nr;
}
Number_Obj nr = parse_number();
```
The above would be the encouraged pattern for such simple cases.
### Allocate new AST Nodes in functions that can throw
There is a major caveat with the previous example, considering this
more real-life implementation that throws an error. The throw may
happen deep down in another function. Holding raw pointers that
we need to free would leak in this case.
```c++
int parse_integer() {
... // do the parsing
if (error) throw(error);
return 42;
}
```
With this `parse_integer` function the previous example would leak memory.
I guess it is pretty obvious, as the allocated memory will not be freed,
as it was never assigned to a SharedObj value. Therefore the above code
would better be written as:
```c++
typedef Number* Number_Ptr;
int parse_integer() {
... // do the parsing
if (error) throw(error);
return 42;
}
// this leaks due to pointer return
// should return Number_Obj instead
// though not possible for virtuals!
Number_Ptr parse_number() {
Number_Obj nr = SASS_MEMORY_NEW(...);
nr->value(parse_integer()); // throws
return &nr; // Ptr from Obj
}
Number_Obj nr = parse_number();
// will now be freed automatically
```
The example above unfortunately will not work as is, since we return a
`Number_Ptr` from that function. Therefore the object allocated inside
the function is already gone when it is picked up again by the caller.
The easy fix for the given simplified use case would be to change the
return type of `parse_number` to `Number_Obj`. Indeed we do it exactly
this way in the parser. But as stated above, this will not work for
virtual functions due to invalid covariant return types!
### Return managed objects from virtual functions
The easy fix would be to just create a new copy on the heap and return
that. But this seems like a very inelegant solution to this problem. I
mean why can't we just tell the object to treat it like a newly allocated
object? And indeed we can. I've added a `detach` method that will tell
the object to survive deallocation until the next pickup. This means
that it will leak if it is not picked up by consumer.
```c++
typedef Number* Number_Ptr;
int parse_integer() {
... // do the parsing
if (error) throw(error);
return 42;
}
Number_Ptr parse_number() {
Number_Obj nr = SASS_MEMORY_NEW(...);
nr->value(parse_integer()); // throws
return nr.detach();
}
Number_Obj nr = parse_number();
// will now be freed automatically
```
## Compile time debug options
To enable memory debugging you need to define `DEBUG_SHARED_PTR`.
This can i.e. be done in `include/sass/base.h`
```c++
define DEBUG_SHARED_PTR
```
This will print lost memory on exit to stderr. You can also use
`setDbg(true)` on sepecific variables to emit reference counter
increase, decrease and other events.
## Why reinvent the wheel when there is `shared_ptr` from C++11
First, implementing a smart pointer class is not really that hard. It
was indeed also a learning experience for myself. But there are more
profound advantages:
- Better GCC 4.4 compatibility (which most code still has OOTB)
- Not thread safe (give us some free performance on some compiler)
- Beeing able to track memory allocations for debugging purposes
- Adding additional features if needed (as seen in `detach`)
- Optional: optimized weak pointer implementation possible
### Thread Safety
As said above, this is not thread safe currently. But we don't need
this ATM anyway. And I guess we probably never will share AST Nodes
across different threads.
\ No newline at end of file
#ifndef SASS_FILE_H
#define SASS_FILE_H
#include <string>
#include <vector>
#include "sass/context.h"
#include "ast_fwd_decl.hpp"
namespace Sass {
namespace File {
// return the current directory
// always with forward slashes
std::string get_cwd();
// test if path exists and is a file
bool file_exists(const std::string& file);
// return if given path is absolute
// works with *nix and windows paths
bool is_absolute_path(const std::string& path);
// return only the directory part of path
std::string dir_name(const std::string& path);
// return only the filename part of path
std::string base_name(const std::string&);
// do a locigal clean up of the path
// no physical check on the filesystem
std::string make_canonical_path (std::string path);
// join two path segments cleanly together
// but only if right side is not absolute yet
std::string join_paths(std::string root, std::string name);
// if the relative path is outside of the cwd we want want to
// show the absolute path in console messages
std::string path_for_console(const std::string& rel_path, const std::string& abs_path, const std::string& orig_path);
// create an absolute path by resolving relative paths with cwd
std::string rel2abs(const std::string& path, const std::string& base = ".", const std::string& cwd = get_cwd());
// create a path that is relative to the given base directory
// path and base will first be resolved against cwd to make them absolute
std::string abs2rel(const std::string& path, const std::string& base = ".", const std::string& cwd = get_cwd());
// helper function to resolve a filename
// searching without variations in all paths
std::string find_file(const std::string& file, struct Sass_Compiler* options);
std::string find_file(const std::string& file, const std::vector<std::string> paths);
// helper function to resolve a include filename
// this has the original resolve logic for sass include
std::string find_include(const std::string& file, const std::vector<std::string> paths);
// split a path string delimited by semicolons or colons (OS dependent)
std::vector<std::string> split_path_list(const char* paths);
// try to load the given filename
// returned memory must be freed
// will auto convert .sass files
char* read_file(const std::string& file);
}
// requested import
class Importer {
public:
// requested import path
std::string imp_path;
// parent context path
std::string ctx_path;
// base derived from context path
// this really just acts as a cache
std::string base_path;
public:
Importer(std::string imp_path, std::string ctx_path)
: imp_path(File::make_canonical_path(imp_path)),
ctx_path(File::make_canonical_path(ctx_path)),
base_path(File::dir_name(ctx_path))
{ }
};
// a resolved include (final import)
class Include : public Importer {
public:
// resolved absolute path
std::string abs_path;
// is a deprecated file type
bool deprecated;
public:
Include(const Importer& imp, std::string abs_path, bool deprecated)
: Importer(imp), abs_path(abs_path), deprecated(deprecated)
{ }
Include(const Importer& imp, std::string abs_path)
: Importer(imp), abs_path(abs_path), deprecated(false)
{ }
};
// a loaded resource
class Resource {
public:
// the file contents
char* contents;
// conected sourcemap
char* srcmap;
public:
Resource(char* contents, char* srcmap)
: contents(contents), srcmap(srcmap)
{ }
};
// parsed stylesheet from loaded resource
class StyleSheet : public Resource {
public:
// parsed root block
Block_Obj root;
public:
StyleSheet(const Resource& res, Block_Obj root)
: Resource(res), root(root)
{ }
};
namespace File {
static std::vector<std::string> defaultExtensions = { ".scss", ".sass" };
std::vector<Include> resolve_includes(const std::string& root, const std::string& file,
const std::vector<std::string>& exts = defaultExtensions);
}
}
#endif
There are several implementations of `libsass` for a variety of languages. Here are just a few of them. Note, some implementations may or may not be up to date. We have not verified whether they work.
### C
* [sassc](https://github.com/hcatlin/sassc)
### Crystal
* [sass.cr](https://github.com/straight-shoota/sass.cr)
### Elixir
* [sass.ex](https://github.com/scottdavis/sass.ex)
### Go
* [go-libsass](https://github.com/wellington/go-libsass)
* [go_sass](https://github.com/suapapa/go_sass)
* [go-sass](https://github.com/SamWhited/go-sass)
### Haskell
* [hLibsass](https://github.com/jakubfijalkowski/hlibsass)
* [hSass](https://github.com/jakubfijalkowski/hsass)
### Java
* [libsass-maven-plugin](https://github.com/warmuuh/libsass-maven-plugin)
* [jsass](https://github.com/bit3/jsass)
### JavaScript
* [sass.js](https://github.com/medialize/sass.js)
### Lua
* [lua-sass](https://github.com/craigbarnes/lua-sass)
### .NET
* [libsass-net](https://github.com/darrenkopp/libsass-net)
* [NSass](https://github.com/TBAPI-0KA/NSass)
* [Sass.Net](https://github.com/andyalm/Sass.Net)
* [SharpScss](https://github.com/xoofx/SharpScss)
* [LibSassHost](https://github.com/Taritsyn/LibSassHost)
### Nim
* [nim-sass](https://github.com/zacharycarter/nim-sass)
### node.js
* [node-sass](https://github.com/sass/node-sass)
### Perl
* [CSS::Sass](https://github.com/caldwell/CSS-Sass)
* [Text::Sass::XS](https://github.com/ysasaki/Text-Sass-XS)
### PHP
* [sassphp](https://github.com/sensational/sassphp)
* [php-sass](https://github.com/lesstif/php-sass)
### Python
* [libsass-python](https://github.com/dahlia/libsass-python)
* [SassPython](https://github.com/marianoguerra/SassPython)
* [pylibsass](https://github.com/rsenk330/pylibsass)
* [python-scss](https://github.com/pistolero/python-scss)
### Ruby
* [sassruby](https://github.com/hcatlin/sassruby)
### Scala
* [Sass-Scala](https://github.com/kkung/Sass-Scala)
### Tcl
* [tclsass](https://github.com/flightaware/tclsass)
#include "sass.hpp"
#include "operators.hpp"
namespace Sass {
namespace Operators {
inline double add(double x, double y) { return x + y; }
inline double sub(double x, double y) { return x - y; }
inline double mul(double x, double y) { return x * y; }
inline double div(double x, double y) { return x / y; } // x/0 checked by caller
inline double mod(double x, double y) { // x/0 checked by caller
if ((x > 0 && y < 0) || (x < 0 && y > 0)) {
double ret = std::fmod(x, y);
return ret ? ret + y : ret;
} else {
return std::fmod(x, y);
}
}
typedef double (*bop)(double, double);
bop ops[Sass_OP::NUM_OPS] = {
0, 0, // and, or
0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte
add, sub, mul, div, mod
};
/* static function, has no pstate or traces */
bool eq(Expression_Obj lhs, Expression_Obj rhs)
{
// operation is undefined if one is not a number
if (!lhs || !rhs) throw Exception::UndefinedOperation(lhs, rhs, Sass_OP::EQ);
// use compare operator from ast node
return *lhs == *rhs;
}
/* static function, throws OperationError, has no pstate or traces */
bool cmp(Expression_Obj lhs, Expression_Obj rhs, const Sass_OP op)
{
// can only compare numbers!?
Number_Obj l = Cast<Number>(lhs);
Number_Obj r = Cast<Number>(rhs);
// operation is undefined if one is not a number
if (!l || !r) throw Exception::UndefinedOperation(lhs, rhs, op);
// use compare operator from ast node
return *l < *r;
}
/* static functions, throws OperationError, has no pstate or traces */
bool lt(Expression_Obj lhs, Expression_Obj rhs) { return cmp(lhs, rhs, Sass_OP::LT); }
bool neq(Expression_Obj lhs, Expression_Obj rhs) { return eq(lhs, rhs) == false; }
bool gt(Expression_Obj lhs, Expression_Obj rhs) { return !cmp(lhs, rhs, Sass_OP::GT) && neq(lhs, rhs); }
bool lte(Expression_Obj lhs, Expression_Obj rhs) { return cmp(lhs, rhs, Sass_OP::LTE) || eq(lhs, rhs); }
bool gte(Expression_Obj lhs, Expression_Obj rhs) { return !cmp(lhs, rhs, Sass_OP::GTE) || eq(lhs, rhs); }
/* static function, throws OperationError, has no traces but optional pstate for returned value */
Value_Ptr op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)
{
enum Sass_OP op = operand.operand;
String_Quoted_Ptr lqstr = Cast<String_Quoted>(&lhs);
String_Quoted_Ptr rqstr = Cast<String_Quoted>(&rhs);
std::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt));
std::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt));
if (Cast<Null>(&lhs)) throw Exception::InvalidNullOperation(&lhs, &rhs, op);
if (Cast<Null>(&rhs)) throw Exception::InvalidNullOperation(&lhs, &rhs, op);
std::string sep;
switch (op) {
case Sass_OP::ADD: sep = ""; break;
case Sass_OP::SUB: sep = "-"; break;
case Sass_OP::DIV: sep = "/"; break;
case Sass_OP::EQ: sep = "=="; break;
case Sass_OP::NEQ: sep = "!="; break;
case Sass_OP::LT: sep = "<"; break;
case Sass_OP::GT: sep = ">"; break;
case Sass_OP::LTE: sep = "<="; break;
case Sass_OP::GTE: sep = ">="; break;
default:
throw Exception::UndefinedOperation(&lhs, &rhs, op);
break;
}
if (op == Sass_OP::ADD) {
// create string that might be quoted on output (but do not unquote what we pass)
return SASS_MEMORY_NEW(String_Quoted, pstate, lstr + rstr, 0, false, true);
}
// add whitespace around operator
// but only if result is not delayed
if (sep != "" && delayed == false) {
if (operand.ws_before) sep = " " + sep;
if (operand.ws_after) sep = sep + " ";
}
if (op == Sass_OP::SUB || op == Sass_OP::DIV) {
if (lqstr && lqstr->quote_mark()) lstr = quote(lstr);
if (rqstr && rqstr->quote_mark()) rstr = quote(rstr);
}
return SASS_MEMORY_NEW(String_Constant, pstate, lstr + sep + rstr);
}
/* static function, throws OperationError, has no traces but optional pstate for returned value */
Value_Ptr op_colors(enum Sass_OP op, const Color& lhs, const Color& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)
{
if (lhs.a() != rhs.a()) {
throw Exception::AlphaChannelsNotEqual(&lhs, &rhs, op);
}
if (op == Sass_OP::DIV && (!rhs.r() || !rhs.g() || !rhs.b())) {
throw Exception::ZeroDivisionError(lhs, rhs);
}
return SASS_MEMORY_NEW(Color,
pstate,
ops[op](lhs.r(), rhs.r()),
ops[op](lhs.g(), rhs.g()),
ops[op](lhs.b(), rhs.b()),
lhs.a());
}
/* static function, throws OperationError, has no traces but optional pstate for returned value */
Value_Ptr op_numbers(enum Sass_OP op, const Number& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)
{
double lval = lhs.value();
double rval = rhs.value();
if (op == Sass_OP::MOD && rval == 0) {
return SASS_MEMORY_NEW(String_Quoted, pstate, "NaN");
}
if (op == Sass_OP::DIV && rval == 0) {
std::string result(lval ? "Infinity" : "NaN");
return SASS_MEMORY_NEW(String_Quoted, pstate, result);
}
size_t l_n_units = lhs.numerators.size();
size_t l_d_units = lhs.numerators.size();
size_t r_n_units = rhs.denominators.size();
size_t r_d_units = rhs.denominators.size();
// optimize out the most common and simplest case
if (l_n_units == r_n_units && l_d_units == r_d_units) {
if (l_n_units + l_d_units <= 1 && r_n_units + r_d_units <= 1) {
if (lhs.numerators == rhs.numerators) {
if (lhs.denominators == rhs.denominators) {
Number_Ptr v = SASS_MEMORY_COPY(&lhs);
v->value(ops[op](lval, rval));
return v;
}
}
}
}
Number_Obj v = SASS_MEMORY_COPY(&lhs);
if (lhs.is_unitless() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {
v->numerators = rhs.numerators;
v->denominators = rhs.denominators;
}
if (op == Sass_OP::MUL) {
v->value(ops[op](lval, rval));
v->numerators.insert(v->numerators.end(),
rhs.numerators.begin(), rhs.numerators.end()
);
v->denominators.insert(v->denominators.end(),
rhs.denominators.begin(), rhs.denominators.end()
);
v->reduce();
}
else if (op == Sass_OP::DIV) {
v->value(ops[op](lval, rval));
v->numerators.insert(v->numerators.end(),
rhs.denominators.begin(), rhs.denominators.end()
);
v->denominators.insert(v->denominators.end(),
rhs.numerators.begin(), rhs.numerators.end()
);
v->reduce();
}
else {
Number ln(lhs), rn(rhs);
ln.reduce(); rn.reduce();
double f(rn.convert_factor(ln));
v->value(ops[op](lval, rn.value() * f));
}
v->pstate(pstate);
return v.detach();
}
/* static function, throws OperationError, has no traces but optional pstate for returned value */
Value_Ptr op_number_color(enum Sass_OP op, const Number& lhs, const Color& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)
{
double lval = lhs.value();
switch (op) {
case Sass_OP::ADD:
case Sass_OP::MUL: {
return SASS_MEMORY_NEW(Color,
pstate,
ops[op](lval, rhs.r()),
ops[op](lval, rhs.g()),
ops[op](lval, rhs.b()),
rhs.a());
}
case Sass_OP::SUB:
case Sass_OP::DIV: {
std::string color(rhs.to_string(opt));
return SASS_MEMORY_NEW(String_Quoted,
pstate,
lhs.to_string(opt)
+ sass_op_separator(op)
+ color);
}
default: break;
}
throw Exception::UndefinedOperation(&lhs, &rhs, op);
}
/* static function, throws OperationError, has no traces but optional pstate for returned value */
Value_Ptr op_color_number(enum Sass_OP op, const Color& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)
{
double rval = rhs.value();
if (op == Sass_OP::DIV && rval == 0) {
// comparison of Fixnum with Float failed?
throw Exception::ZeroDivisionError(lhs, rhs);
}
return SASS_MEMORY_NEW(Color,
pstate,
ops[op](lhs.r(), rval),
ops[op](lhs.g(), rval),
ops[op](lhs.b(), rval),
lhs.a());
}
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
Plugins are shared object files (.so on *nix and .dll on win) that can be loaded by LibSass on runtime. Currently we only provide a way to load internal/custom functions from plugins. In the future we probably will also add a way to provide custom importers via plugins (needs more refactoring to [support multiple importers with some kind of priority system](https://github.com/sass/libsass/issues/962)).
## plugin.cpp
```C++
#include <cstring>
#include <iostream>
#include <stdint.h>
#include "sass_values.h"
union Sass_Value* ADDCALL call_fn_foo(const union Sass_Value* s_args, void* cookie)
{
// we actually abuse the void* to store an "int"
return sass_make_number((intptr_t)cookie, "px");
}
extern "C" const char* ADDCALL libsass_get_version() {
return libsass_version();
}
extern "C" Sass_C_Function_List ADDCALL libsass_load_functions()
{
// allocate a custom function caller
Sass_C_Function_Callback fn_foo =
sass_make_function("foo()", call_fn_foo, (void*)42);
// create list of all custom functions
Sass_C_Function_List fn_list = sass_make_function_list(1);
// put the only function in this plugin to the list
sass_function_set_list_entry(fn_list, 0, fn_foo);
// return the list
return fn_list;
}
```
To compile the plugin you need to have LibSass already built as a shared library (to link against it). The commands below expect the shared library in the `lib` sub-directory (`-Llib`). The plugin and the main LibSass process should "consume" the same shared LibSass library on runtime. It will propably also work if they use different LibSass versions. In this case we check if the major versions are compatible (i.e. 3.1.3 and 3.1.1 would be considered compatible).
## Compile with gcc on linux
```bash
g++ -O2 -shared plugin.cpp -o plugin.so -fPIC -Llib -lsass
```
## Compile with mingw on windows
```bash
g++ -O2 -shared plugin.cpp -o plugin.dll -Llib -lsass
```
#include "sass.hpp"
#include <cstdlib>
#include <cstring>
#include <vector>
#include <sstream>
#include "sass.h"
#include "file.hpp"
#include "util.hpp"
#include "sass_context.hpp"
#include "sass_functions.hpp"
namespace Sass {
// helper to convert string list to vector
std::vector<std::string> list2vec(struct string_list* cur)
{
std::vector<std::string> list;
while (cur) {
list.push_back(cur->string);
cur = cur->next;
}
return list;
}
}
extern "C" {
using namespace Sass;
// Allocate libsass heap memory
// Don't forget string termination!
void* ADDCALL sass_alloc_memory(size_t size)
{
void* ptr = malloc(size);
if (ptr == NULL) {
std::cerr << "Out of memory.\n";
exit(EXIT_FAILURE);
}
return ptr;
}
char* ADDCALL sass_copy_c_string(const char* str)
{
size_t len = strlen(str) + 1;
char* cpy = (char*) sass_alloc_memory(len);
std::memcpy(cpy, str, len);
return cpy;
}
// Deallocate libsass heap memory
void ADDCALL sass_free_memory(void* ptr)
{
if (ptr) free (ptr);
}
// caller must free the returned memory
char* ADDCALL sass_string_quote (const char *str, const char quote_mark)
{
std::string quoted = quote(str, quote_mark);
return sass_copy_c_string(quoted.c_str());
}
// caller must free the returned memory
char* ADDCALL sass_string_unquote (const char *str)
{
std::string unquoted = unquote(str);
return sass_copy_c_string(unquoted.c_str());
}
char* ADDCALL sass_compiler_find_include (const char* file, struct Sass_Compiler* compiler)
{
// get the last import entry to get current base directory
Sass_Import_Entry import = sass_compiler_get_last_import(compiler);
const std::vector<std::string>& incs = compiler->cpp_ctx->include_paths;
// create the vector with paths to lookup
std::vector<std::string> paths(1 + incs.size());
paths.push_back(File::dir_name(import->abs_path));
paths.insert( paths.end(), incs.begin(), incs.end() );
// now resolve the file path relative to lookup paths
std::string resolved(File::find_include(file, paths));
return sass_copy_c_string(resolved.c_str());
}
char* ADDCALL sass_compiler_find_file (const char* file, struct Sass_Compiler* compiler)
{
// get the last import entry to get current base directory
Sass_Import_Entry import = sass_compiler_get_last_import(compiler);
const std::vector<std::string>& incs = compiler->cpp_ctx->include_paths;
// create the vector with paths to lookup
std::vector<std::string> paths(1 + incs.size());
paths.push_back(File::dir_name(import->abs_path));
paths.insert( paths.end(), incs.begin(), incs.end() );
// now resolve the file path relative to lookup paths
std::string resolved(File::find_file(file, paths));
return sass_copy_c_string(resolved.c_str());
}
// Make sure to free the returned value!
// Incs array has to be null terminated!
// this has the original resolve logic for sass include
char* ADDCALL sass_find_include (const char* file, struct Sass_Options* opt)
{
std::vector<std::string> vec(list2vec(opt->include_paths));
std::string resolved(File::find_include(file, vec));
return sass_copy_c_string(resolved.c_str());
}
// Make sure to free the returned value!
// Incs array has to be null terminated!
char* ADDCALL sass_find_file (const char* file, struct Sass_Options* opt)
{
std::vector<std::string> vec(list2vec(opt->include_paths));
std::string resolved(File::find_file(file, vec));
return sass_copy_c_string(resolved.c_str());
}
// Get compiled libsass version
const char* ADDCALL libsass_version(void)
{
return LIBSASS_VERSION;
}
// Get compiled libsass version
const char* ADDCALL libsass_language_version(void)
{
return LIBSASS_LANGUAGE_VERSION;
}
}
namespace Sass {
// helper to aid dreaded MSVC debug mode
char* sass_copy_string(std::string str)
{
// In MSVC the following can lead to segfault:
// sass_copy_c_string(stream.str().c_str());
// Reason is that the string returned by str() is disposed before
// sass_copy_c_string is invoked. The string is actually a stack
// object, so indeed nobody is holding on to it. So it seems
// perfectly fair to release it right away. So the const char*
// by c_str will point to invalid memory. I'm not sure if this is
// the behavior for all compiler, but I'm pretty sure we would
// have gotten more issues reported if that would be the case.
// Wrapping it in a functions seems the cleanest approach as the
// function must hold on to the stack variable until it's done.
return sass_copy_c_string(str.c_str());
}
}
\ No newline at end of file
#ifndef SASS_SASS_CONTEXT_H
#define SASS_SASS_CONTEXT_H
#include "sass/base.h"
#include "sass/context.h"
#include "ast_fwd_decl.hpp"
// sass config options structure
struct Sass_Options : Sass_Output_Options {
// embed sourceMappingUrl as data uri
bool source_map_embed;
// embed include contents in maps
bool source_map_contents;
// create file urls for sources
bool source_map_file_urls;
// Disable sourceMappingUrl in css output
bool omit_source_map_url;
// Treat source_string as sass (as opposed to scss)
bool is_indented_syntax_src;
// The input path is used for source map
// generation. It can be used to define
// something with string compilation or to
// overload the input file path. It is
// set to "stdin" for data contexts and
// to the input file on file contexts.
char* input_path;
// The output path is used for source map
// generation. LibSass will not write to
// this file, it is just used to create
// information in source-maps etc.
char* output_path;
// Colon-separated list of paths
// Semicolon-separated on Windows
// Maybe use array interface instead?
char* extension;
char* include_path;
char* plugin_path;
// Extensions (linked string list)
struct string_list* extensions;
// Include paths (linked string list)
struct string_list* include_paths;
// Plugin paths (linked string list)
struct string_list* plugin_paths;
// Path to source map file
// Enables source map generation
// Used to create sourceMappingUrl
char* source_map_file;
// Directly inserted in source maps
char* source_map_root;
// Custom functions that can be called from sccs code
Sass_Function_List c_functions;
// List of custom importers
Sass_Importer_List c_importers;
// List of custom headers
Sass_Importer_List c_headers;
};
// base for all contexts
struct Sass_Context : Sass_Options
{
// store context type info
enum Sass_Input_Style type;
// generated output data
char* output_string;
// generated source map json
char* source_map_string;
// error status
int error_status;
char* error_json;
char* error_text;
char* error_message;
// error position
char* error_file;
size_t error_line;
size_t error_column;
const char* error_src;
// report imported files
char** included_files;
};
// struct for file compilation
struct Sass_File_Context : Sass_Context {
// no additional fields required
// input_path is already on options
};
// struct for data compilation
struct Sass_Data_Context : Sass_Context {
// provided source string
char* source_string;
char* srcmap_string;
};
// link c and cpp context
struct Sass_Compiler {
// progress status
Sass_Compiler_State state;
// original c context
Sass_Context* c_ctx;
// Sass::Context
Sass::Context* cpp_ctx;
// Sass::Block
Sass::Block_Obj root;
};
#endif
## Requirements
In order to install and setup your local development environment, there are some prerequisites:
* git
* gcc/clang/llvm (Linux: build tools, Mac OS X: XCode w/ Command Line Tools)
* ruby w/ bundler
OS X:
First you'll need to install XCode which you can now get from the AppStore installed on your mac. After you download that and run it, then run this on the command line:
````
xcode-select --install
````
## Cloning the Projects
First, clone the project and then add a line to your `~/.bash_profile` that will let other programs know where the LibSass dev files are.
````
git clone git@github.com:sass/libsass.git
cd libsass
echo "export SASS_LIBSASS_PATH=$(pwd)" >> ~/.bash_profile
````
Then, if you run the "bootstrap" script, it should clone all the other required projects.
````
./script/bootstrap
````
You should now have a `sass-spec` and `sassc` folder within the libsass folder. Both of these are clones of their respective git projects. If you want to do a pull request, remember to work in those folders. For instance, if you want to add a test (see other documentation for how to do that), make sure to commit it to your *fork* of the sass-spec github project. Also, whenever you are running tests, make sure to `pull` from the origin! We want to make sure we are testing against the newest libsass, sassc, and sass-spec!
Now, try and see if you can build the project. We do that with the `make` command.
````
make
````
At this point, if you get an error, something is most likely wrong with your compiler installation. Yikes. It's hard to cover how to fix this in an article. Feel free to open an issue and we'll try and help! But, remember, before you do that, googling the error message is your friend! Many problems are solved quickly that way.
## Running The Spec Against LibSass
Then, to run the spec against LibSass, just run:
````
./script/spec
````
If you get an error about `SASS_LIBSASS_PATH`, you may still need to set a variable pointing to the libsass folder, like this:
````
export SASS_LIBSASS_PATH=/Users/you/path/libsass
````
...where the latter part is to the `libsass` directory you've cloned. You can get this path by typing `pwd` in the Terminal
## Running the Spec Against Ruby Sass
Go into the sass-spec folder that should have been cloned earlier with the "bootstrap" command. Run the following.
````
bundle install
./sass-spec.rb
````
Voila! Now you are testing against Sass too!
This document is mainly intended for developers!
# Documenting some of the source map internals
Since source maps are somewhat a black box to all LibSass maintainers, [I](@mgreter) will try to document my findings with source maps in LibSass, as I come across them. This document will also brievely explain how LibSass parses the source and how it outputs the result.
The main storage for SourceMap mappings is the `mappings` vector:
```
# in source_map.hpp
vector<Mapping> mappings
# in mappings.hpp
struct Mapping ...
Position original_position;
Position generated_position;
```
## Every parsed token has its source associated
LibSass uses a lexical parser. Whenever LibSass finds a token of interest, it creates a specific `AST_Node`, which will hold a reference to the input source with line/column information. `AST_Node` is the base class for all parsed items. They are declared in `ast.hpp` and are used in `parser.hpp`. Here a simple example:
```
if (lex< custom_property_name >()) {
Sass::String* prop = new (ctx.mem) String_Constant(path, source_position, lexed);
return new (ctx.mem) Declaration(path, prop->position(), prop, ...);
}
```
## How is the `source_position` calculated
This is automatically done with `lex` in `parser.hpp`. Whenever something is lexed, the `source_position` is updated. But be aware that `source_position` points to the begining of the parsed text. If you need a mapping for the position where the parsing ended, you need to add another call to `lex` (to match nothing)!
```
lex< exactly < empty_str > >();
end = new (ctx.mem) String_Constant(path, source_position, lexed);
```
## How are mappings for the output created
So far we have collected all needed data for all tokens in the input stream. We can now use this information to create mappings when we put things into the output stream. Mappings are created via the `add_mappings` method:
```
# in source_map.hpp
void add_mapping(AST_Node* node);
```
This method is called in two places:
- `Inspect::append_to_buffer`
- `Output_[Nested|Compressed]::append_to_buffer`
Mappings can only be created for things that have been parsed into a `AST_Node`. Otherwise we do not have the information to create the mappings, which is the reason why LibSass currently only maps the most important tokens in source maps.
$@ $TEST_FLAGS --tap --silent | tapout tap
## This is proposed interface in https://github.com/sass/libsass/pull/1288
Additional debugging macros with low overhead are available, `TRACE()` and `TRACEINST()`.
Both macros simulate a string stream, so they can be used like this:
TRACE() << "Reached.";
produces:
[LibSass] parse_value parser.cpp:1384 Reached.
`TRACE()`
logs function name, source filename, source file name to the standard error and the attached
stream to the standard error.
`TRACEINST(obj)`
logs object instance address, function name, source filename, source file name to the standard error and the attached stream to the standard error, for example:
TRACEINST(this) << "String_Constant created " << this;
produces:
[LibSass] 0x8031ba980:String_Constant ./ast.hpp:1371 String_Constant created (0,"auto")
The macros generate output only of `LibSass_TRACE` is set in the environment.
This is an article about how to help with LibSass issues. Issue triage is a fancy word for explaining how we deal with incoming issues and make sure that the right problems get worked on. The lifecycle of an issue goes like this:
1. Issue is reported by a user.
2. If the issue seems like a bug, then the "bug" tag is added.
3. If the reporting user didn't also create a spec test over at sass/sass-spec, the "needs test" tag is added.
4. Verify that Ruby Sass *does not* have the same bug. LibSass strives to be an exact replica of how Ruby Sass works. If it's an issue that neither project has solved, please close the ticket with the "not in sass" label.
5. The smallest possible breaking test is created in sass-spec. Cut away any extra information or non-breaking code until the core issue is made clear.
6. Again, verify that the expected output matches the latest Ruby Sass release. Do this by using your own tool OR by running ./sass-spec.rb in the spec folder and making sure that your test passes!
7. Create the test cases in sass-spec with the name spec/LibSass-todo-issues/issue_XXX/input.scss and expected_output.css where the XXX is the issue number here.
8. Commit that test to sass-spec, making sure to reference the issue in the comment message like "Test to demonstrate sass/LibSass#XXX".
9. Once the spec test exists, remove the "needs test" tag and replace it with "test written".
10. A C++ developer will then work on the issue and issue a pull request to fix the issue.
11. A core member verifies that the fix does actually fix the spec tests.
12. The fix is merged into the project.
13. The spec is moved from the LibSass-todo-issues folder into LibSass-closed-issues
14. The issue is closed
15. Have a soda pop or enjoyable beverage of your choice
LibSass currently expects all input to be utf8 encoded (and outputs only utf8), if you actually have any unicode characters at all. We do not support conversion between encodings, even if you declare it with a `@charset` rule. The text below was originally posted as an [issue](https://github.com/sass/libsass/issues/381) on the LibSass tracker. Since then the status is outdated as LibSass now expects your
input to be utf8/ascii compatible, as it has been proven that reading ANSI (e.g. single byte encodings) as utf8 can lead to unexpected
behavior, which can in the worst case lead to buffer overruns/segfaults. Therefore LibSass now checks your input to be valid utf8 encoded!
### [Declaring character encodings in CSS](http://www.w3.org/International/questions/qa-css-charset.en)
This [explains](http://www.w3.org/International/questions/qa-css-charset.en) how the character encoding of a css file is determined. Since we are only dealing with local files, we never have a HTTP header. So the precedence should be 'charset' rule, byte-order mark (BOM) or auto-detection (finally falling back to system default/UTF-8). This may not sound too hard to implement, but what about import rules? The CSS specs do not forbid the mixing of different encodings! I [solved that](https://github.com/mgreter/webmerge/) by converting all files to UTF-8 internally. On writing there is an option to tell the tool what encoding it should be (UTF-8 by default). One can also define if it should write a BOM or not and if it should add the charset declaration.
Since my [tool]((https://github.com/mgreter/webmerge/)) is written in perl, I have a lot of utilities at hand to deal with different unicode charsets. I'm pretty sure that most OSS uses [ICU](http://site.icu-project.org/) or [libiconv](https://www.gnu.org/software/libiconv/) to convert between different encodings. But I have now idea how easy/hard this would be to integrate platform independent (it seems doable). ANSII (single byte encoding) to utf8 is basically just a conversion table (for every supported code-page).
### Current status on LibSass unicode support
LibSass should/is fully UTF (and therefore plain ASCII) compatible.
~~Currently LibSass seems to handle the common UTF-8 case pretty well. I believe it should correctly support all ASCII compatible encodings (like UTF-8 or Latin-1). If all includes use the same encoding, the output should be correct (in the same encoding). It should also handle unicode chars in [selectors, variable names and other identifiers](https://github.com/hcatlin/libsass/issues/244#issuecomment-34681227). This is true for all ASCII compatible encodings. So the main incompatible encodings (I'm aware of) are UTF-16/UTF-32 (which could be converted to UTF-8 with libiconv).~~
LibSass 3.5 will enforce that your input is either plain ASCII (chars below 127) or utf8. It does not handle anything else, but therefore ensures that the output is in a valid form. Before version 3.5 you were able to mix different code-pages, which yielded unexpected behavior.
### Current encoding auto detection
LibSass currently reads all kind of BOMs and will error out if it finds something it doesn't know how to handle! It seems that it throws away the optional UTF-8 BOM (if any is found). IMO it would be nice if users could configure that (also if a charset rule should be added to the output). But it does not really take any `@charset` into account, it always assumes your input is utf8 and ignores any given `@charset`!
### What is currently not supported
- Using non ASCII compatible encodings (like UTF-16, Latin-1 etc.)
- Using non ASCII characters in different encodings in different includes
### What is missing to support the above cases
- A way to convert between encodings (like libiconv/ICU)
- Sniffing the charset inside the file (source is available)
- Handling the conversion on import (and export)
- Optional: Make output encoding configurable
- Optional: Add optional/mandatory BOM (configurable)
### Low priority feature
I guess the current implementation should handle more than 99% of all real world use cases.
A) Unicode characters are still seldomly seen (as they can be written escaped)
~~B) It will still work if it's UTF-8 or in any of the most common known western ISO codepages.
Although I'm not sure how this applies to asian and other "exotic" codepages!~~
I guess the biggest Problem is to have libiconv/ICU (or some other) library as a dependency. Since it contains a lot of rules for the conversions, I see it as the only way to handle this correctly. Once that is sorted out it should be pretty much straight forward to implement the missing pieces (in parser.cpp - Parser::parse should return encoding and add Parser::sniff_charset, then convert the source byte stream to UTF-8).
I hope the statements above all hold true. Unicode is really not the easiest topic to wrap your head around. But since I did all the above recently in Perl, I wanted to document it here. Feel free to extend or criticize.
#ifndef SASS_UTIL_H
#define SASS_UTIL_H
#include <vector>
#include <string>
#include <assert.h>
#include "sass.hpp"
#include "sass/base.h"
#include "ast_fwd_decl.hpp"
#define SASS_ASSERT(cond, msg) assert(cond && msg)
namespace Sass {
double round(double val, size_t precision = 0);
double sass_strtod(const char* str);
const char* safe_str(const char *, const char* = "");
void free_string_array(char **);
char **copy_strings(const std::vector<std::string>&, char ***, int = 0);
std::string read_css_string(const std::string& str, bool css = true);
std::string evacuate_escapes(const std::string& str);
std::string string_to_output(const std::string& str);
std::string comment_to_string(const std::string& text);
std::string read_hex_escapes(const std::string& str);
std::string escape_string(const std::string& str);
void newline_to_space(std::string& str);
std::string quote(const std::string&, char q = 0);
std::string unquote(const std::string&, char* q = 0, bool keep_utf8_sequences = false, bool strict = true);
char detect_best_quotemark(const char* s, char qm = '"');
bool is_hex_doublet(double n);
bool is_color_doublet(double r, double g, double b);
bool peek_linefeed(const char* start);
namespace Util {
std::string rtrim(const std::string& str);
std::string normalize_underscores(const std::string& str);
std::string normalize_decimals(const std::string& str);
bool isPrintable(Ruleset_Ptr r, Sass_Output_Style style = NESTED);
bool isPrintable(Supports_Block_Ptr r, Sass_Output_Style style = NESTED);
bool isPrintable(Media_Block_Ptr r, Sass_Output_Style style = NESTED);
bool isPrintable(Comment_Ptr b, Sass_Output_Style style = NESTED);
bool isPrintable(Block_Obj b, Sass_Output_Style style = NESTED);
bool isPrintable(String_Constant_Ptr s, Sass_Output_Style style = NESTED);
bool isPrintable(String_Quoted_Ptr s, Sass_Output_Style style = NESTED);
bool isPrintable(Declaration_Ptr d, Sass_Output_Style style = NESTED);
bool isAscii(const char chr);
}
}
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment