Skip to content

Commit

Permalink
Implement third party plugins loader
Browse files Browse the repository at this point in the history
Currently only for custom functions.
More features to be added later ...
  • Loading branch information
mgreter committed Mar 6, 2015
1 parent 9a3899a commit eda5558
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 eda5558

Please sign in to comment.