Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link C/C++ stdlib statically for binary gems #127

Merged
merged 7 commits into from Jun 5, 2019
Merged

Conversation

glebm
Copy link
Contributor

@glebm glebm commented May 27, 2019

Also:

  1. Compile via mkmf instead of libsass's own Makefile. This is necessary to make cross-compilation work (rake-compiler hooks into mkmf).
  2. Do not use per-ruby versions of libsass.so. It is unnecessary, because libsass.so is Ruby-agnostic (the FFI gem is used instead to use it from any Ruby version).
  3. Add VERSION file to the non-precompiled gem (as the git information is not available when installing).
  4. Clean before every build.

@glebm glebm force-pushed the static branch 3 times, most recently from 5923584 to 33f9b8b Compare May 27, 2019 17:59
@glebm glebm changed the title Link C++ stlib statically for binary gems Link C/C++ stdlib statically for binary gems May 27, 2019
@glebm
Copy link
Contributor Author

glebm commented May 27, 2019

@bolandrm This should fix all the issues reported so far.

I don't have access to a Mac though, so please test on one if possible.

@johnfairh
Copy link

Fails on macOS (10.14.5/Xcode 10.2/Ruby 2.5.0) at runtime because native.rb looks for libsass.bundle in #{gem_root}/lib/sassc but the shared lib is installed to #{gem_root}/lib. This is from master + this PR then rake install.

Works fine after bodging the path but I don't know about other platforms or what your intention is here - putting it in lib/sassc looks like part of the deleted LIBSASS_OUT stuff. Looking at the travis log there is an install -c that sorts this out for the rake test mode.

@betesh
Copy link

betesh commented May 28, 2019

Putting VERSION into ext/libsass seems to break cloning the libsass submodule:

Submodule 'ext/libsass' (https://github.com/sass/libsass.git) 
registered for path 'ext/libsass'
fatal: destination path 
'~/my_app/vendor/bundle/jruby/2.3.0/bundler/gems/sassc-ruby-33f9b8b3eb02/ext/libsass' 
already exists and is not an empty directory.

This is with gem "sassc", github: "glebm/sassc-ruby", branch: "static" in my Gemfile.

Maybe we can create VERSION after cloning the submodule?

@glebm
Copy link
Contributor Author

glebm commented May 29, 2019

Both issues should be fixed now, please try again!

@johnfairh
Copy link

LGTM.

@betesh
Copy link

betesh commented May 29, 2019

I got further this time...

creating Makefile

current directory:
~/my_app/vendor/bundle/jruby/2.3.0/bundler/gems/sassc-ruby-9de3520574e5/ext
make "DESTDIR=" clean

current directory:
~/my_app/vendor/bundle/jruby/2.3.0/bundler/gems/sassc-ruby-9de3520574e5/ext
make "DESTDIR="
compiling ./libsass/src/cencode.c
cc: error: cencode.o: No such file or directory
Makefile:203: recipe for target 'cencode.o' failed
make: *** [cencode.o] Error 1

make failed, exit code 2

here's the Makefile it generated:


SHELL = /bin/sh

# V=0 quiet, V=1 verbose.  other values don't work.
V = 0
Q1 = $(V:1=)
Q = $(Q1:0=@)
ECHO1 = $(V:1=@)
ECHO = $(ECHO1:0=@echo)
NULLCMD = 

#### Start of system configuration section. ####

srcdir = .
topdir = uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib
hdrdir = uri:classloader:/META-INF/jruby.home/lib/ruby/include
arch_hdrdir = $(extout)/include/$(arch)
PATH_SEPARATOR = :
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby:$(srcdir)/libsass/src:$(srcdir)/libsass/src/utf8/:$(srcdir)/libsass/src/support/:$(srcdir)/libsass/src/b64/:$(srcdir)/libsass/src/memory/
prefix = $(DESTDIR)\ 
libdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib
rubylibdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib
rubysharedlibdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/ruby/shared
sitedir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/ruby/2.3/site_ruby
sitelibdir = $(DESTDIR)./.gem.20190529-18043-1pixjfy
sitearchdir = $(DESTDIR)./.gem.20190529-18043-1pixjfy
archdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/native/include
datadir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/share
mandir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/man
sysconfdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/etc
localstatedir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/var
includedir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/native/include
rubyhdrdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/native/include


CC = cc
CXX = c++
LIBRUBY = 
LIBRUBY_A = 
LIBRUBYARG_SHARED = 
LIBRUBYARG_STATIC = 
empty =
OUTFLAG = -o $(empty)
COUTFLAG = $(empty)

RUBY_EXTCONF_H = 
cflags   = 
cxxflags = 
optflags = 
debugflags = 
warnflags = 
CCDLFLAGS = 
CFLAGS   = $(CCDLFLAGS)  -fPIC  -fno-omit-frame-pointer -fno-strict-aliasing  -fexceptions $(cflags) -DLIBSASS_VERSION='"3.6.0"' $(ARCH_FLAG)
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir) -I$(srcdir)/libsass/include
DEFS     = 
CPPFLAGS =   $(DEFS) $(cppflags)
CXXFLAGS = $(CCDLFLAGS)  -fno-omit-frame-pointer -fno-strict-aliasing  -fexceptions $(cflags) $(cxxflags) -std=c++11 -DLIBSASS_VERSION='"3.6.0"' $(ARCH_FLAG)
ldflags  = 
dldflags =  
ARCH_FLAG =  -m64
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
LDSHARED = cc -shared 
LDSHAREDXX = c++ -shared 
AR = 
EXEEXT = 

RUBY_SO_NAME = jruby

arch = java
sitearch = java
ruby_version = 2.3.0
ruby = $(bindir)/jruby
RUBY = $(ruby)
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h

RM = rm -f
RM_RF = $(RUBY) -run -e rm -- -rf
RMDIRS = $(RUBY) -run -e rmdir -- -p
MAKEDIRS = mkdir -p 
INSTALL = install -c 
INSTALL_PROG = $(INSTALL) -m 0755
INSTALL_DATA = $(INSTALL) -m 0644
COPY = cp 
TOUCH = exit >

#### End of system configuration section. ####

preload = 

libpath = . $(libdir)
LIBPATH =  -L. -L$(libdir)
DEFFILE = 

CLEANFILES = mkmf.log
DISTCLEANFILES = 
DISTCLEANDIRS = 

extout = 
extout_prefix = 
target_prefix = /sassc
LOCAL_LIBS = 
LIBS =    
ORIG_SRCS = 
SRCS = $(ORIG_SRCS) cencode.c c99func.c error_handling.cpp util.cpp position.cpp inspect.cpp node.cpp plugins.cpp fn_maps.cpp lexer.cpp fn_strings.cpp ast_sel_cmp.cpp ast_values.cpp fn_selectors.cpp util_string.cpp bind.cpp emitter.cpp output.cpp eval.cpp values.cpp cssize.cpp parser.cpp sass.cpp sass_util.cpp fn_lists.cpp units.cpp ast2c.cpp extend.cpp context.cpp base64vlq.cpp backtrace.cpp json.cpp to_value.cpp ast_selectors.cpp subset_map.cpp fn_miscs.cpp utf8_string.cpp sass2scss.cpp fn_colors.cpp ast_sel_unify.cpp fn_utils.cpp sass_values.cpp ast_supports.cpp ast_fwd_decl.cpp check_nesting.cpp sass_functions.cpp file.cpp c2ast.cpp expand.cpp ast.cpp sass_context.cpp prelexer.cpp source_map.cpp remove_placeholders.cpp operators.cpp constants.cpp fn_numbers.cpp color_maps.cpp environment.cpp listize.cpp SharedPtr.cpp
OBJS = cencode.o c99func.o error_handling.o util.o position.o inspect.o node.o plugins.o fn_maps.o lexer.o fn_strings.o ast_sel_cmp.o ast_values.o fn_selectors.o util_string.o bind.o emitter.o output.o eval.o values.o cssize.o parser.o sass.o sass_util.o fn_lists.o units.o ast2c.o extend.o context.o base64vlq.o backtrace.o json.o to_value.o ast_selectors.o subset_map.o fn_miscs.o utf8_string.o sass2scss.o fn_colors.o ast_sel_unify.o fn_utils.o sass_values.o ast_supports.o ast_fwd_decl.o check_nesting.o sass_functions.o file.o c2ast.o expand.o ast.o sass_context.o prelexer.o source_map.o remove_placeholders.o operators.o constants.o fn_numbers.o color_maps.o environment.o listize.o SharedPtr.o
HDRS = 
TARGET = libsass
TARGET_NAME = libsass
TARGET_ENTRY = Init_$(TARGET_NAME)
DLLIB = $(TARGET).so
EXTSTATIC = 
STATIC_LIB = 

TIMESTAMP_DIR = .
BINDIR        = $(bindir)
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
RUBYLIBDIR    = $(sitelibdir)$(target_prefix)
RUBYARCHDIR   = $(sitearchdir)$(target_prefix)
HDRDIR        = $(rubyhdrdir)/ruby$(target_prefix)
ARCHHDRDIR    = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)

TARGET_SO     = $(DLLIB)
CLEANLIBS     = $(TARGET).so 
CLEANOBJS     = *.o  *.bak

all:    $(DLLIB)
static: $(STATIC_LIB) install-rb
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-static clean-rb

clean-static::
clean-rb-default::
clean-rb::
clean-so::
clean: clean-so clean-static clean-rb-default clean-rb
		-$(Q)$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time

distclean-rb-default::
distclean-rb::
distclean-so::
distclean-static::
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
		-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
		-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
		-$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true

realclean: distclean
install: install-so install-rb

install-so: $(DLLIB) $(TIMESTAMP_DIR)/.RUBYARCHDIR.-.sassc.time
	$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
clean-static::
	-$(Q)$(RM) $(STATIC_LIB)
install-rb: pre-install-rb install-rb-default
install-rb-default: pre-install-rb-default
pre-install-rb: Makefile
pre-install-rb-default: Makefile
pre-install-rb-default:
	@$(NULLCMD)
$(TIMESTAMP_DIR)/.RUBYARCHDIR.-.sassc.time:
	$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
	$(Q) $(TOUCH) $@

site-install: site-install-so site-install-rb
site-install-so: install-so
site-install-rb: install-rb

.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S

.cc.o:
	$(ECHO) compiling $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.cc.S:
	$(ECHO) translating $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<

.mm.o:
	$(ECHO) compiling $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.mm.S:
	$(ECHO) translating $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<

.cxx.o:
	$(ECHO) compiling $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.cxx.S:
	$(ECHO) translating $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<

.cpp.o:
	$(ECHO) compiling $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.cpp.S:
	$(ECHO) translating $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<

.c.o:
	$(ECHO) compiling $(<)
	$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<

.c.S:
	$(ECHO) translating $(<)
	$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $<

.m.o:
	$(ECHO) compiling $(<)
	$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<

.m.S:
	$(ECHO) translating $(<)
	$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $<

$(DLLIB): $(OBJS) Makefile
	$(ECHO) linking shared-object sassc/$(DLLIB)
	-$(Q)$(RM) $(@)
	$(Q) $(LDSHAREDXX) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
	$(Q) strip -x $@



###
# This file is used by mkmf.
# By default, dependencies include Ruby headers but we don't need them.
$(OBJS): $(HDRS)

@glebm
Copy link
Contributor Author

glebm commented May 29, 2019

@betesh I see from the Makefile you're using JRuby.

This is a bug in JRuby, I've reported it here: jruby/jruby#5749

I've worked around the bug in this commit: 2ae4b7c

Perhaps your attempt was prior to the work-around commit?

@betesh
Copy link

betesh commented May 29, 2019

Perhaps your attempt was prior to the work-around commit?

Look in the output I pasted above -- you can see that the directory it's cloning to contains the SHA 9de3520, so definitely post-workaround.

@betesh
Copy link

betesh commented May 29, 2019

I just tried again with 2ae4b7c, same results

@betesh
Copy link

betesh commented May 29, 2019

FWIW I'm using jruby 9.1.17.0 (latest in the 9.1 series)

@glebm
Copy link
Contributor Author

glebm commented May 29, 2019

Ah, the RUBY_PLATFORM == 'jruby' check was wrong (I've added it after testing on JRUBY, the correct check is RUBY_PLATFORM == 'java'). Should work now, hopefully. 😄

@betesh
Copy link

betesh commented May 29, 2019

Thanks for all the quick responses and the hard work of trying to make this work in every environment. We're getting much closer...

Output

creating Makefile

current directory:
~/my_app/vendor/bundle/jruby/2.3.0/bundler/gems/sassc-ruby-6d56c34655d8/ext
make "DESTDIR=" clean

current directory:
~/my_app/vendor/bundle/jruby/2.3.0/bundler/gems/sassc-ruby-6d56c34655d8/ext
make "DESTDIR="
compiling ./libsass/src/cencode.c
compiling ./libsass/src/c99func.c
compiling ./libsass/src/error_handling.cpp
compiling ./libsass/src/util.cpp
compiling ./libsass/src/position.cpp
compiling ./libsass/src/inspect.cpp
compiling ./libsass/src/node.cpp
compiling ./libsass/src/plugins.cpp
compiling ./libsass/src/fn_maps.cpp
compiling ./libsass/src/lexer.cpp
compiling ./libsass/src/fn_strings.cpp
compiling ./libsass/src/ast_sel_cmp.cpp
compiling ./libsass/src/ast_values.cpp
compiling ./libsass/src/fn_selectors.cpp
compiling ./libsass/src/util_string.cpp
compiling ./libsass/src/bind.cpp
compiling ./libsass/src/emitter.cpp
compiling ./libsass/src/output.cpp
compiling ./libsass/src/eval.cpp
compiling ./libsass/src/values.cpp
compiling ./libsass/src/cssize.cpp
compiling ./libsass/src/parser.cpp
compiling ./libsass/src/sass.cpp
compiling ./libsass/src/sass_util.cpp
compiling ./libsass/src/fn_lists.cpp
compiling ./libsass/src/units.cpp
compiling ./libsass/src/ast2c.cpp
compiling ./libsass/src/extend.cpp
compiling ./libsass/src/context.cpp
compiling ./libsass/src/base64vlq.cpp
compiling ./libsass/src/backtrace.cpp
compiling ./libsass/src/json.cpp
compiling ./libsass/src/to_value.cpp
compiling ./libsass/src/ast_selectors.cpp
compiling ./libsass/src/subset_map.cpp
compiling ./libsass/src/fn_miscs.cpp
compiling ./libsass/src/utf8_string.cpp
compiling ./libsass/src/sass2scss.cpp
compiling ./libsass/src/fn_colors.cpp
compiling ./libsass/src/ast_sel_unify.cpp
compiling ./libsass/src/fn_utils.cpp
compiling ./libsass/src/sass_values.cpp
compiling ./libsass/src/ast_supports.cpp
compiling ./libsass/src/ast_fwd_decl.cpp
compiling ./libsass/src/check_nesting.cpp
compiling ./libsass/src/sass_functions.cpp
compiling ./libsass/src/file.cpp
compiling ./libsass/src/c2ast.cpp
compiling ./libsass/src/expand.cpp
compiling ./libsass/src/ast.cpp
compiling ./libsass/src/sass_context.cpp
compiling ./libsass/src/prelexer.cpp
compiling ./libsass/src/source_map.cpp
compiling ./libsass/src/remove_placeholders.cpp
compiling ./libsass/src/operators.cpp
compiling ./libsass/src/constants.cpp
compiling ./libsass/src/fn_numbers.cpp
compiling ./libsass/src/color_maps.cpp
compiling ./libsass/src/environment.cpp
compiling ./libsass/src/listize.cpp
compiling ./libsass/src/memory/SharedPtr.cpp
linking shared-object sassc/libsass.so
/usr/bin/ld: error_handling.o: relocation R_X86_64_32 against
`_ZTVN4Sass9Exception4BaseE' can not be used when making a shared object;
recompile with -fPIC
error_handling.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
Makefile:219: recipe for target 'libsass.so' failed
make: *** [libsass.so] Error 1

make failed, exit code 2

Makefile

SHELL = /bin/sh

# V=0 quiet, V=1 verbose.  other values don't work.
V = 0
Q1 = $(V:1=)
Q = $(Q1:0=@)
ECHO1 = $(V:1=@)
ECHO = $(ECHO1:0=@echo)
NULLCMD = 

#### Start of system configuration section. ####

srcdir = .
topdir = uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib
hdrdir = uri:classloader:/META-INF/jruby.home/lib/ruby/include
arch_hdrdir = $(extout)/include/$(arch)
PATH_SEPARATOR = :
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby:$(srcdir)/libsass/src:$(srcdir)/libsass/src/utf8/:$(srcdir)/libsass/src/support/:$(srcdir)/libsass/src/b64/:$(srcdir)/libsass/src/memory/
prefix = $(DESTDIR)\ 
libdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib
rubylibdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib
rubysharedlibdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/ruby/shared
sitedir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/ruby/2.3/site_ruby
sitelibdir = $(DESTDIR)./.gem.20190529-31525-1hal0go
sitearchdir = $(DESTDIR)./.gem.20190529-31525-1hal0go
archdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/native/include
datadir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/share
mandir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/man
sysconfdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/etc
localstatedir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/var
includedir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/native/include
rubyhdrdir = $(DESTDIR)uri:classloader:/META-INF/jruby.home/lib/native/include


CC = cc
CXX = c++
LIBRUBY = 
LIBRUBY_A = 
LIBRUBYARG_SHARED = 
LIBRUBYARG_STATIC = 
empty =
OUTFLAG = -o $(empty)
COUTFLAG = -o $(empty)$(empty)

RUBY_EXTCONF_H = 
cflags   = 
cxxflags = 
optflags = 
debugflags = 
warnflags = 
CCDLFLAGS = 
CFLAGS   = $(CCDLFLAGS)  -fPIC  -fno-omit-frame-pointer -fno-strict-aliasing  -fexceptions $(cflags) -DLIBSASS_VERSION='"3.6.0"' $(ARCH_FLAG)
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir) -I$(srcdir)/libsass/include
DEFS     = 
CPPFLAGS =   $(DEFS) $(cppflags)
CXXFLAGS = $(CCDLFLAGS)  -fno-omit-frame-pointer -fno-strict-aliasing  -fexceptions $(cflags) $(cxxflags) -std=c++11 -DLIBSASS_VERSION='"3.6.0"' $(ARCH_FLAG)
ldflags  = 
dldflags =  
ARCH_FLAG =  -m64
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
LDSHARED = cc -shared 
LDSHAREDXX = c++ -shared 
AR = 
EXEEXT = 

RUBY_SO_NAME = jruby

arch = java
sitearch = java
ruby_version = 2.3.0
ruby = $(bindir)/jruby
RUBY = $(ruby)
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h

RM = rm -f
RM_RF = $(RUBY) -run -e rm -- -rf
RMDIRS = $(RUBY) -run -e rmdir -- -p
MAKEDIRS = mkdir -p 
INSTALL = install -c 
INSTALL_PROG = $(INSTALL) -m 0755
INSTALL_DATA = $(INSTALL) -m 0644
COPY = cp 
TOUCH = exit >

#### End of system configuration section. ####

preload = 

libpath = . $(libdir)
LIBPATH =  -L. -L$(libdir)
DEFFILE = 

CLEANFILES = mkmf.log
DISTCLEANFILES = 
DISTCLEANDIRS = 

extout = 
extout_prefix = 
target_prefix = /sassc
LOCAL_LIBS = 
LIBS =    
ORIG_SRCS = 
SRCS = $(ORIG_SRCS) cencode.c c99func.c error_handling.cpp util.cpp position.cpp inspect.cpp node.cpp plugins.cpp fn_maps.cpp lexer.cpp fn_strings.cpp ast_sel_cmp.cpp ast_values.cpp fn_selectors.cpp util_string.cpp bind.cpp emitter.cpp output.cpp eval.cpp values.cpp cssize.cpp parser.cpp sass.cpp sass_util.cpp fn_lists.cpp units.cpp ast2c.cpp extend.cpp context.cpp base64vlq.cpp backtrace.cpp json.cpp to_value.cpp ast_selectors.cpp subset_map.cpp fn_miscs.cpp utf8_string.cpp sass2scss.cpp fn_colors.cpp ast_sel_unify.cpp fn_utils.cpp sass_values.cpp ast_supports.cpp ast_fwd_decl.cpp check_nesting.cpp sass_functions.cpp file.cpp c2ast.cpp expand.cpp ast.cpp sass_context.cpp prelexer.cpp source_map.cpp remove_placeholders.cpp operators.cpp constants.cpp fn_numbers.cpp color_maps.cpp environment.cpp listize.cpp SharedPtr.cpp
OBJS = cencode.o c99func.o error_handling.o util.o position.o inspect.o node.o plugins.o fn_maps.o lexer.o fn_strings.o ast_sel_cmp.o ast_values.o fn_selectors.o util_string.o bind.o emitter.o output.o eval.o values.o cssize.o parser.o sass.o sass_util.o fn_lists.o units.o ast2c.o extend.o context.o base64vlq.o backtrace.o json.o to_value.o ast_selectors.o subset_map.o fn_miscs.o utf8_string.o sass2scss.o fn_colors.o ast_sel_unify.o fn_utils.o sass_values.o ast_supports.o ast_fwd_decl.o check_nesting.o sass_functions.o file.o c2ast.o expand.o ast.o sass_context.o prelexer.o source_map.o remove_placeholders.o operators.o constants.o fn_numbers.o color_maps.o environment.o listize.o SharedPtr.o
HDRS = 
TARGET = libsass
TARGET_NAME = libsass
TARGET_ENTRY = Init_$(TARGET_NAME)
DLLIB = $(TARGET).so
EXTSTATIC = 
STATIC_LIB = 

TIMESTAMP_DIR = .
BINDIR        = $(bindir)
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
RUBYLIBDIR    = $(sitelibdir)$(target_prefix)
RUBYARCHDIR   = $(sitearchdir)$(target_prefix)
HDRDIR        = $(rubyhdrdir)/ruby$(target_prefix)
ARCHHDRDIR    = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)

TARGET_SO     = $(DLLIB)
CLEANLIBS     = $(TARGET).so 
CLEANOBJS     = *.o  *.bak

all:    $(DLLIB)
static: $(STATIC_LIB) install-rb
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-static clean-rb

clean-static::
clean-rb-default::
clean-rb::
clean-so::
clean: clean-so clean-static clean-rb-default clean-rb
		-$(Q)$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time

distclean-rb-default::
distclean-rb::
distclean-so::
distclean-static::
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
		-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
		-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
		-$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true

realclean: distclean
install: install-so install-rb

install-so: $(DLLIB) $(TIMESTAMP_DIR)/.RUBYARCHDIR.-.sassc.time
	$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
clean-static::
	-$(Q)$(RM) $(STATIC_LIB)
install-rb: pre-install-rb install-rb-default
install-rb-default: pre-install-rb-default
pre-install-rb: Makefile
pre-install-rb-default: Makefile
pre-install-rb-default:
	@$(NULLCMD)
$(TIMESTAMP_DIR)/.RUBYARCHDIR.-.sassc.time:
	$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
	$(Q) $(TOUCH) $@

site-install: site-install-so site-install-rb
site-install-so: install-so
site-install-rb: install-rb

.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S

.cc.o:
	$(ECHO) compiling $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.cc.S:
	$(ECHO) translating $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<

.mm.o:
	$(ECHO) compiling $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.mm.S:
	$(ECHO) translating $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<

.cxx.o:
	$(ECHO) compiling $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.cxx.S:
	$(ECHO) translating $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<

.cpp.o:
	$(ECHO) compiling $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<

.cpp.S:
	$(ECHO) translating $(<)
	$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<

.c.o:
	$(ECHO) compiling $(<)
	$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<

.c.S:
	$(ECHO) translating $(<)
	$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $<

.m.o:
	$(ECHO) compiling $(<)
	$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<

.m.S:
	$(ECHO) translating $(<)
	$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $<

$(DLLIB): $(OBJS) Makefile
	$(ECHO) linking shared-object sassc/$(DLLIB)
	-$(Q)$(RM) $(@)
	$(Q) $(LDSHAREDXX) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
	$(Q) strip -x $@



###
# This file is used by mkmf.
# By default, dependencies include Ruby headers but we don't need them.
$(OBJS): $(HDRS)

@glebm
Copy link
Contributor Author

glebm commented May 29, 2019

Ah, I see what's going on here. Another bug in JRuby, reported here jruby/jruby#5751 and will push a work-around shortly

@glebm
Copy link
Contributor Author

glebm commented May 29, 2019

Pushed a work-around, let's see if it works. Thanks for all the testing!

@ahorek
Copy link

ahorek commented May 29, 2019

works, thanks :)

@betesh
Copy link

betesh commented May 29, 2019

Works for me too! Thanks @glebm !

Also:
1. Compile via `mkmf` instead of libsass's own Makefile.
   This is necessary to make cross-compilation work (rake-compiler hooks
   into mkmf).
2. Do not use per-ruby versions of `libsass.so`. It is unnecessary,
   because `libsass.so` is Ruby-agnostic (the FFI gem is used instead to
   use it from any Ruby version).
3. Add VERSION file to the non-precompiled gem.
4. Clean before every build.
@glebm
Copy link
Contributor Author

glebm commented May 29, 2019

Thanks for testing!

@bolandrm Bugs and commits squashed, should be good to go! 🎉

@betesh
Copy link

betesh commented May 29, 2019

Turns out all of that was enough to get it working on Ubuntu (16.04.6) but not on CentOS (7.5.1804). On a CentOS docker image, I'm hitting this error:

Using sassc 2.1.0.pre1 from https://github.com/glebm/sassc-ruby.git
(at /root/my_app/vendor/cache/sassc-ruby-5a6cedaeeafd@5a6ceda)
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

current directory:
/root/my_app/vendor/cache/sassc-ruby-5a6cedaeeafd/ext
/usr/local/rvm/rubies/jruby-9.1.17.0/bin/jruby -r
./siteconf20190529-8-188cn8w.rb extconf.rb
creating Makefile

current directory:
/root/my_app/vendor/cache/sassc-ruby-5a6cedaeeafd/ext
make "DESTDIR=" clean

current directory:
/root/my_app/vendor/cache/sassc-ruby-5a6cedaeeafd/ext
make "DESTDIR="
compiling ./libsass/src/cencode.c
compiling ./libsass/src/c99func.c
compiling ./libsass/src/utf8_string.cpp
In file included from ./libsass/src/sass.hpp:55:0,
                 from ./libsass/src/utf8_string.cpp:3:
/usr/include/c++/4.8.2/string:38:28: fatal error: bits/c++config.h: No such file
or directory
 #include <bits/c++config.h>
                            ^
compilation terminated.
make: *** [utf8_string.o] Error 1

make failed, exit code 2

I have a feeling there's a package I'm missing. I'm trying to track it down...

@glebm
Copy link
Contributor Author

glebm commented May 29, 2019

@betesh Perhaps libstdc++-devel?

@glebm
Copy link
Contributor Author

glebm commented May 29, 2019

NB: There is an issue with cross-compiled binary gems overriding Ruby versions restrictions. Looking into it.

-march=native is only enabled when not cross-compiling
@glebm
Copy link
Contributor Author

glebm commented May 29, 2019

NB: There is an issue with cross-compiled binary gems overriding Ruby versions restrictions. Looking into it.

Fixed

@betesh
Copy link

betesh commented May 29, 2019

@betesh Perhaps libstdc++-devel?

That seems to have fixed it. Thanks again @glebm.

Is there a way we can make sassc-ruby ensure that that's installed?

@glebm
Copy link
Contributor Author

glebm commented May 29, 2019

No, it's a system package (different on every distro, and one of many options). A pre-requisite for compiling libsass is a working C++ compilation environment (that package is the C++ standard library).

On Linux, the pre-compiled gem will be installed by default on x86/64 anyway.

@glebm
Copy link
Contributor Author

glebm commented Jun 4, 2019

@bolandrm Can you please merge this, #122, and #124, and release a new version?

@bolandrm bolandrm merged commit 4f98263 into sass:master Jun 5, 2019
@bolandrm
Copy link
Member

bolandrm commented Jun 5, 2019

Version 2.1.0.pre2 released. Please give it a test!

@glebm glebm deleted the static branch June 5, 2019 16:10
@eregon
Copy link
Contributor

eregon commented Jun 12, 2019

This broke installation of the gem on TruffleRuby unfortunately, which I recently just made pass all the sassc-ruby tests by completing the FFI implementation.

Compiling a non-C extension with mkmf seems a bit weird, notably when knowing that TruffleRuby compiles C extensions to bitcode to run them and that doesn't work for accessing the library with FFI later.
Would it be OK to restore the original simple Makefile for the non-binary gem/cross compiling case?

I guess I should have been faster to add TruffleRuby in sassc-ruby's TravisCI.

@glebm
Copy link
Contributor Author

glebm commented Jun 12, 2019

The easiest way I found to make cross-compilation work correctly was with mkmf. The previous Makefile version didn't actually work (all the pre1 binary releases have a Linux 64-bit .so).

Can Truffle Ruby check that $LIBRUBYARG == nil and compile normally instead of bitcode?

I've also asked for a better API than $LIBRUBY = nil on Ruby's issue tracker (https://bugs.ruby-lang.org/issues/15914).

@eregon
Copy link
Contributor

eregon commented Jun 13, 2019

Can Truffle Ruby check that $LIBRUBYARG == nil and compile normally instead of bitcode?

I think that's not really feasible, because at that point mkmf is loaded and already captured the values from RbConfig which tell it to compile to bitcode.

What might help is compiling in TruffleRuby with native + bitcode, but I'm not sure when that's going to land and the sassc gem wouldn't work until then on TruffleRuby.

Do you think it would make sense to change libsass's Makefile, so it has the necessary tweaking arguments that are needed for cross/static compilation? I think that would be generally useful, as other languages might want cross/static compilation too.

I've also asked for a better API than $LIBRUBY = nil on Ruby's issue tracker (https://bugs.ruby-lang.org/issues/15914).

Thanks for filing that.

@glebm
Copy link
Contributor Author

glebm commented Jun 13, 2019

Do you think it would make sense to change libsass's Makefile

When compiling libraries for shared linking into ruby, we want to use the same compiler and flags as specified in RbConfig. So we'd have to plumb these values from RbConfig into the Makefile.

Then, we'd have to duplicate functionality that rake-compiler provides for the correct cross-compilation: rake-compiler does this by tweaking mkmf with a fake RbConfig, so that mkmf picks up the cross-compiler flags instead of the native RbConfig ones.

Finally, we'd need to build our own replacement for the rake-compiler-dock to make sure cross-compilation always runs in the same environment (slightly older version of libc for compatibility, etc).

This seems like a lot of work, and quite error-prone.

I think that would be generally useful, as other languages might want cross/static compilation too.

Perhaps, but other languages seem to use their own mkmf equivalents for compiling libsass, e.g.:

As a work-around, perhaps TruffleRuby could expose a way to disable bitcode compilation? Then we could do something like this in sassc-ruby/ext/extconf.rb:

$truffle_ruby_disable_bitcode_compilation = true
require 'mkmf'

@eregon
Copy link
Contributor

eregon commented Jun 13, 2019

Thanks for the detailed reply.
Of course, I understand reusing rake-compiler, rake-compiler-dock, etc is convenient and doesn't need to reinvent the wheel. Nothing against that.

When compiling libraries for shared linking into ruby, we want to use the same compiler and flags as specified in RbConfig.

What do you mean by "shared linking into ruby"? Isn't libsass always compiled as a shared library, and loaded via Ruby-FFI?

I'm not sure about this: why should it be the same compiler and flags than ruby? My thinking is if libsass wants to use different flags or even conflicting flags with ruby's flags (e.g., different warnings or optimizations flags), it shouldn't matter and it should be free to do so.
OTOH, if it works currently, I guess it doesn't matter too much.

Then, we'd have to duplicate functionality that rake-compiler provides for the correct cross-compilation

That functionality seems to just set RUBY_PLATFORM, RUBY_VERSION and a couple other constants. But if we provide our own Makefile I would think these constants are not even used at all by extconf.rb.

I would guess rake-compiler only assumes that extconf.rb creates a Makefile, and that it can give a few flags to the Makefile when building for cross-compilation. But I might be wrong, it's just a guess, I'll take a deeper look.

As a work-around, perhaps TruffleRuby could expose a way to disable bitcode compilation? Then we could do something like this in sassc-ruby/ext/extconf.rb:

That might work, I'll need to try. Could be fairly hacky if RbConfig constants need to be rewritten (that's where the flags for bitcode are).
The original Makefile is fairly trivial, so I think that could still be a cleaner fix.
I'm hopeful we can compile to both native and bitcode in TruffleRuby, and then we shouldn't need any change in sassc.

@glebm
Copy link
Contributor Author

glebm commented Jun 13, 2019

What do you mean by "shared linking into ruby"? Isn't libsass always compiled as a shared library, and loaded via Ruby-FFI?

libsass.so itself links C++ and C standard libraries. When the gem is compiled from source on the user's computer, it links them dynamically. If the Ruby installation or another so also links them dynamically, it may not work if these stdlibs are different. Some examples:

  • CC set to clang but ruby uses gcc.
  • Multiple versions of gcc are installed and RbConfig does not use the default one.

This is why it's important that the RbConfig values are used during compilation. Note that this doesn't apply to binary gems, as these link stdlib statically. This has its own disadvantages and should not be done when installing from source, e.g. system-wide stdlib security updates to stdlib won't apply, dtrace may not work correctly, etc. This is why it's only done for precompiled gems (we have to for compatibility).

I would guess rake-compiler only assumes that extconf.rb creates a Makefile, and that it can give a few flags to the Makefile

Not quite, rake-compiler patches mkmf to use a "fake" RbConfig with cross-compilation settings.

https://github.com/rake-compiler/rake-compiler/blob/18b335a87000efe91db8997f586772150528f342/lib/rake/extensiontask.rb#L402-L423

The Makefile that ruby generates doesn't accept any external variables (all assignments use = instead of ?=) IIRC.

That might work, I'll need to try. Could be fairly hacky if RbConfig constants need to be rewritten (that's where the flags for bitcode are).

A minimal hack that I can think of is to clone the entire RbConfig, set constants on the clone, and then only do RbConfig = BitCodeRbConfig unless $truffle_ruby_disable_bitcode_compilation.

I'm hopeful we can compile to both native and bitcode in TruffleRuby, and then we shouldn't need any change in sassc.

Hopefully Ruby accepts my feature request for mkmf, and then you can just check mkmf's flag. The "all global variables and constants" design of mkmf does make it a pain to change or customize. 😩

@eregon
Copy link
Contributor

eregon commented Jun 17, 2019

I can confirm installing the pre-compiled pre-release works on TruffleRuby, nice.
Installing from source also works when compiling to both bitcode and native (that's work in progress).
I think that's good enough for now.
@glebm Thank you for your comments.

eregon added a commit to eregon/sassc-ruby that referenced this pull request Nov 24, 2019
* Since libsass is meant to be used with FFI, RbConfig::CONFIG['SOEXT']
  would be better, but DLEXT is used as libsass is compiled by mkmf (sass#127).
* Use RbConfig::MAKEFILE_CONFIG['DLEXT'] instead of just RbConfig::CONFIG['DLEXT']
  so it's also correct on JRuby and not 'jar'. See
  https://github.com/rake-compiler/rake-compiler/blob/f808dd7a/lib/rake/extensiontask.rb#L41
* This lets Ruby implementations use another file extension than .so/.bundle
  for C extensions. For example, TruffleRuby 19.3.0 uses .dylib on macOS.
* Some OS also use a different file extension, such as HPUX which uses .sl.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants