Skip to content

Commit

Permalink
Merge pull request sass#919 from mgreter/feature/plugin-functions
Browse files Browse the repository at this point in the history
Implement third party plugin loader
  • Loading branch information
mgreter committed Mar 6, 2015
2 parents e5b604c + eda5558 commit dd4187a
Show file tree
Hide file tree
Showing 14 changed files with 372 additions and 24 deletions.
32 changes: 19 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ else
endif

ifeq "$(LIBSASS_VERSION)" ""
ifneq "$(wildcard ./.git/ )" ""
LIBSASS_VERSION ?= $(shell git describe --abbrev=4 --dirty --always --tags)
endif
ifneq "$(wildcard ./.git/ )" ""
LIBSASS_VERSION ?= $(shell git describe --abbrev=4 --dirty --always --tags)
endif
endif

ifeq "$(LIBSASS_VERSION)" ""
ifneq ("$(wildcard VERSION)","")
LIBSASS_VERSION ?= $(shell $(CAT) VERSION)
endif
ifneq ("$(wildcard VERSION)","")
LIBSASS_VERSION ?= $(shell $(CAT) VERSION)
endif
endif

ifneq "$(LIBSASS_VERSION)" ""
CFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\""
CXXFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\""
CFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\""
CXXFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\""
endif

# enable mandatory flag
Expand All @@ -50,18 +50,18 @@ else
endif

ifneq "$(SASS_LIBSASS_PATH)" ""
CFLAGS += -I $(SASS_LIBSASS_PATH)
CXXFLAGS += -I $(SASS_LIBSASS_PATH)
CFLAGS += -I $(SASS_LIBSASS_PATH)
CXXFLAGS += -I $(SASS_LIBSASS_PATH)
endif

ifneq "$(EXTRA_CFLAGS)" ""
CFLAGS += $(EXTRA_CFLAGS)
CFLAGS += $(EXTRA_CFLAGS)
endif
ifneq "$(EXTRA_CXXFLAGS)" ""
CXXFLAGS += $(EXTRA_CXXFLAGS)
CXXFLAGS += $(EXTRA_CXXFLAGS)
endif
ifneq "$(EXTRA_LDFLAGS)" ""
LDFLAGS += $(EXTRA_LDFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS)
endif

LDLIBS = -lstdc++ -lm
Expand All @@ -71,6 +71,11 @@ ifeq ($(UNAME),Darwin)
LDFLAGS += -stdlib=libc++
endif

ifneq (MinGW,$(UNAME))
LDFLAGS += -ldl
LDLIBS += -ldl
endif

ifneq ($(BUILD),shared)
BUILD = static
endif
Expand Down Expand Up @@ -117,6 +122,7 @@ SOURCES = \
emitter.cpp \
output.cpp \
parser.cpp \
plugins.cpp \
position.cpp \
prelexer.cpp \
remove_placeholders.cpp \
Expand Down
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ else
AM_CFLAGS += -fPIC
AM_CXXFLAGS += -fPIC
AM_CXXFLAGS += -std=c++0x
AM_LDFLAGS += -ldl
endif

AM_CFLAGS += -DLIBSASS_VERSION="\"$(VERSION)\""
Expand Down Expand Up @@ -68,6 +69,7 @@ libsass_la_SOURCES = \
emitter.cpp emitter.hpp \
output.cpp output.hpp \
parser.cpp parser.hpp \
plugins.cpp plugins.hpp \
prelexer.cpp prelexer.hpp \
remove_placeholders.cpp remove_placeholders.hpp \
sass.cpp sass.h \
Expand Down
49 changes: 48 additions & 1 deletion context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
#endif

#include "ast.hpp"
#include "sass.h"
#include "context.hpp"
#include "plugins.hpp"
#include "constants.hpp"
#include "parser.hpp"
#include "file.hpp"
Expand Down Expand Up @@ -51,6 +53,7 @@ namespace Sass {
mem(Memory_Manager<AST_Node>()),
source_c_str (initializers.source_c_str()),
sources (vector<const char*>()),
plugin_paths (initializers.plugin_paths()),
include_paths (initializers.include_paths()),
queue (vector<Sass_Queued>()),
style_sheets (map<string, Block*>()),
Expand All @@ -71,8 +74,10 @@ namespace Sass {
names_to_colors (map<string, Color*>()),
colors_to_names (map<int, string>()),
precision (initializers.precision()),
plugins(),
subset_map (Subset_Map<string, pair<Complex_Selector*, Compound_Selector*> >())
{

cwd = get_cwd();

// enforce some safe defaults
Expand All @@ -83,9 +88,19 @@ namespace Sass {
include_paths.push_back(cwd);
collect_include_paths(initializers.include_paths_c_str());
collect_include_paths(initializers.include_paths_array());
collect_plugin_paths(initializers.plugin_paths_c_str());
collect_plugin_paths(initializers.plugin_paths_array());

setup_color_map();

for (size_t i = 0, S = plugin_paths.size(); i < S; ++i) {
plugins.load_plugins(plugin_paths[i]);
}

for(auto fn : plugins.get_functions()) {
c_functions.push_back(fn);
}

string entry_point = initializers.entry_point();
if (!entry_point.empty()) {
string result(add_file(entry_point));
Expand Down Expand Up @@ -155,14 +170,46 @@ namespace Sass {

void Context::collect_include_paths(const char** paths_array)
{
if (*include_paths.back().rbegin() != '/') include_paths.back() += '/';
if (paths_array) {
for (size_t i = 0; paths_array[i]; i++) {
collect_include_paths(paths_array[i]);
}
}
}

void Context::collect_plugin_paths(const char* paths_str)
{

if (paths_str) {
const char* beg = paths_str;
const char* end = Prelexer::find_first<PATH_SEP>(beg);

while (end) {
string path(beg, end - beg);
if (!path.empty()) {
if (*path.rbegin() != '/') path += '/';
plugin_paths.push_back(path);
}
beg = end + 1;
end = Prelexer::find_first<PATH_SEP>(beg);
}

string path(beg);
if (!path.empty()) {
if (*path.rbegin() != '/') path += '/';
plugin_paths.push_back(path);
}
}
}

void Context::collect_plugin_paths(const char** paths_array)
{
if (paths_array) {
for (size_t i = 0; paths_array[i]; i++) {
collect_plugin_paths(paths_array[i]);
}
}
}
void Context::add_source(string load_path, string abs_path, const char* contents)
{
sources.push_back(contents);
Expand Down
8 changes: 8 additions & 0 deletions context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "source_map.hpp"
#include "subset_map.hpp"
#include "output.hpp"
#include "plugins.hpp"
#include "sass_functions.h"

struct Sass_C_Function_Descriptor;
Expand Down Expand Up @@ -44,6 +45,7 @@ namespace Sass {
vector<string> include_links;
// vectors above have same size

vector<string> plugin_paths; // relative paths to load plugins
vector<string> include_paths; // lookup paths for includes
vector<Sass_Queued> queue; // queue of files to be parsed
map<string, Block*> style_sheets; // map of paths to ASTs
Expand Down Expand Up @@ -80,8 +82,11 @@ namespace Sass {
KWD_ARG(Data, string, indent);
KWD_ARG(Data, string, linefeed);
KWD_ARG(Data, const char*, include_paths_c_str);
KWD_ARG(Data, const char*, plugin_paths_c_str);
KWD_ARG(Data, const char**, include_paths_array);
KWD_ARG(Data, const char**, plugin_paths_array);
KWD_ARG(Data, vector<string>, include_paths);
KWD_ARG(Data, vector<string>, plugin_paths);
KWD_ARG(Data, bool, source_comments);
KWD_ARG(Data, Output_Style, output_style);
KWD_ARG(Data, string, source_map_file);
Expand Down Expand Up @@ -113,11 +118,14 @@ namespace Sass {
vector<string> get_included_files(size_t skip = 0);

private:
void collect_plugin_paths(const char* paths_str);
void collect_plugin_paths(const char** paths_array);
void collect_include_paths(const char* paths_str);
void collect_include_paths(const char** paths_array);
string format_source_mapping_url(const string& file);

string cwd;
Plugins plugins;

// void register_built_in_functions(Env* env);
// void register_function(Signature sig, Native_Function f, Env* env);
Expand Down
155 changes: 155 additions & 0 deletions plugins.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <dlfcn.h>
#endif

#include <iostream>
#include "output.hpp"
#include "plugins.hpp"

#define npos string::npos

namespace Sass {

Plugins::Plugins(void) { }
Plugins::~Plugins(void) { }

// check if plugin is compatible with this version
// plugins may be linked static against libsass
// we try to be compatible between major versions
inline bool compatibility(const char* their_version)
{
// const char* their_version = "3.1.2";
// first check if anyone has an unknown version
const char* our_version = libsass_version();
if (!strcmp(their_version, "[na]")) return false;
if (!strcmp(our_version, "[na]")) return false;

// find the position of the second dot
size_t pos = string(our_version).find('.', 0);
if (pos != npos) pos = string(our_version).find('.', pos + 1);

// if we do not have two dots we fallback to compare complete string
if (pos == npos) { return strcmp(their_version, our_version) ? 0 : 1; }
// otherwise only compare up to the second dot (major versions)
else { return strncmp(their_version, our_version, pos) ? 0 : 1; }

}

// load one specific plugin
bool Plugins::load_plugin (const string& path)
{

typedef const char* (*__plugin_version__)(void);
typedef Sass_C_Function_List (*__plugin_load_fns__)(void);

if (LOAD_LIB(plugin, path))
{
// try to load initial function to query libsass version suppor
if (LOAD_LIB_FN(__plugin_version__, plugin_version, "libsass_get_version"))
{
// get the libsass version of the plugin
if (!compatibility(plugin_version())) return false;
// try to get import address for "libsass_load_functions"
if (LOAD_LIB_FN(__plugin_load_fns__, plugin_load_functions, "libsass_load_functions"))
{
Sass_C_Function_List fns = plugin_load_functions();
while (fns && *fns) { functions.push_back(*fns); ++ fns; }
}
// success
return true;
}
else
{
// print debug message to stderr (should not happen)
cerr << "failed loading 'libsass_support' in <" << path << ">" << endl;
if (const char* dlsym_error = dlerror()) cerr << dlsym_error << endl;
CLOSE_LIB(plugin);
}
}
else
{
// print debug message to stderr (should not happen)
cerr << "failed loading plugin <" << path << ">" << endl;
if (const char* dlopen_error = dlerror()) cerr << dlopen_error << endl;
}

return false;

}

size_t Plugins::load_plugins(const string& path)
{

// count plugins
size_t loaded = 0;

#ifdef _WIN32

try
{

// use wchar (utf16)
WIN32_FIND_DATAW data;
// trailing slash is guaranteed
string globsrch(path + "*.dll");
// convert to wide chars (utf16) for system call
wstring wglobsrch(UTF_8::convert_to_utf16(globsrch));
HANDLE hFile = FindFirstFileW(wglobsrch.c_str(), &data);
// check if system called returned a result
// ToDo: maybe we should print a debug message
if (hFile == INVALID_HANDLE_VALUE) return -1;

// read directory
while (true)
{
try
{
// the system will report the filenames with wide chars (utf16)
string entry = UTF_8::convert_from_utf16(data.cFileName);
// check if file ending matches exactly
if (!ends_with(entry, ".dll")) continue;
// load the plugin and increase counter
if (load_plugin(path + entry)) ++ loaded;
// check if there should be more entries
if (GetLastError() == ERROR_NO_MORE_FILES) break;
// load next entry (check for return type)
if (!FindNextFileW(hFile, &data)) break;
}
catch (...)
{
// report the error to the console (should not happen)
// seems like we got strange data from the system call?
cerr << "filename in plugin path has invalid utf8?" << endl;
}
}
}
catch (utf8::invalid_utf8)
{
// report the error to the console (should not happen)
// implementors should make sure to provide valid utf8
cerr << "plugin path contains invalid utf8" << endl;
}

#else

DIR *dp;
struct dirent *dirp;
if((dp = opendir(path.c_str())) == NULL) return -1;
while ((dirp = readdir(dp)) != NULL) {
if (!ends_with(dirp->d_name, ".so")) continue;
if (load_plugin(path + dirp->d_name)) ++ loaded;
}
closedir(dp);

#endif
return loaded;

}

}

Loading

0 comments on commit dd4187a

Please sign in to comment.