- 前端
- 构建工具
【免费下载链接】node-sass
:rainbow: Node.js bindings to libsass
本篇指南基于 node-sass 仓库内嵌的 libsass 源码构建文档 build-with-makefiles.md 展开,讲解如何用纯 Makefile 方式构建 libsass(静态库libsass.a或动态库libsass.so)、编译命令行工具 sassc,以及运行 sass-spec 规范测试套件。node-sass 正是通过 Node.js binding(见 src/libsass.gyp)链接 libsass 来实现 SCSS 编译的,理解这套构建流程有助于你排查原生编译问题、定制 libsass 编译参数或验证底层 C++ 行为。
获取源码
按原文档的推荐方式,优先使用 git 克隆三个仓库,其中 sassc 和 sass-spec 只在需要编译命令行工具或跑测试套件时才需要:
# 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注意两个子仓库必须克隆到libsass/目录内部,而不是平级目录——Makefile 中 sassc 与 sass-spec 的默认路径就硬编码在源树内:Makefile 定义了
SASS_SASSC_PATH ?= sassc SASS_SPEC_PATH ?= sass-spec SASS_SPEC_SPEC_DIR ?= spec SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc如果你已有一个现成的 libsass 开发环境,也可以跳过手动克隆,直接运行仓库提供的 bootstrap 脚本,它会自动检查并按需克隆这两个仓库:
./script/bootstrap配套文档 setup-environment.md 还建议将 libsass 位置导出为SASS_LIBSASS_PATH,供 sassc 等外部工具定位头文件与库文件。
选择静态库还是动态库
libsass既可以作为static库也可以作为shared库构建和链接,默认是 static。原文档给出两种切换方式:
# 方式一:设置环境变量 export BUILD="shared" # 方式二:调用 make 时直接定义 BUILD="shared" make ...这一行为可以直接在 Makefile 中印证:
ifneq ($(BUILD),shared) BUILD := static endif也就是说,只要BUILD不等于shared,一切其他取值(包括空值)都会回落到static。而all目标最终会解析为static或shared两个伪目标之一(Makefile):
all: $(BUILD) ... static: $(STATICLIB) # lib/libsass.a shared: $(SHAREDLIB) # lib/libsass.so两者的实际产物目标分别是:
lib/libsass.a: lib $(COBJECTS) $(OBJECTS) $(AR) rcvs $@ $(COBJECTS) $(OBJECTS) lib/libsass.so: lib $(COBJECTS) $(OBJECTS) $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(LDLIBS)影响链接方式的几个关联变量
阅读 Makefile 后可以补充以下几个与BUILD强相关的细节:
- 静态构建会追加
-lstdc++:非 shared 构建时需要显式链接 C++ 标准库(Makefile):ifneq ($(BUILD),shared) LDLIBS += -lstdc++ endif - 非 Windows 平台强制
-fPIC:保证对象代码可被动态库复用(Makefile)。 - Windows 特例:Windows 下非 shared 构建会自动启用
STATIC_ALL=1、STATIC_LIBGCC=1、STATIC_LIBSTDCPP=1,即追加-static、-static-libgcc、-static-libstdc++以尽量做静态链接,提高可移植性(代价是体积增大约 50KB,见 Makefile 注释);shared 构建则改用lib/libsass.dll作为共享产物,并定义ADD_EXPORTS宏(Makefile)。 - 调试构建:
DEBUG=1会把 BUILD 改写为debug-static/debug-shared,Makefile 中提供了专门的debug-static、debug-shared目标,追加-g -DDEBUG -DDEBUG_LVL="..."并剔除-O2(Makefile)。
编译库本体
在 libsass 的上级目录执行:
make -C libsass -j5-j5表示 5 路并行编译。编译完成后,产物位于libsass/lib/目录:
$ ls libsass/lib libsass.a libsass.so参与编译的源文件清单
静态库libsass.a由$(COBJECTS) $(OBJECTS)打包而成(Makefile 中$(AR) rcvs $@ ...)。具体包含哪些源文件,由 Makefile.conf 给出,共 45 个 C++ 源文件加 1 个 C 文件:
SOURCES = \ ast.cpp \ node.cpp \ context.cpp \ constants.cpp \ functions.cpp \ ... \ source_map.cpp \ subset_map.cpp \ error_handling.cpp \ memory/SharedPtr.cpp \ utf8_string.cpp \ base64vlq.cpp CSOURCES = cencode.c这些正是 libsass 的核心管线:词法分析(lexer.cpp/prelexer.cpp)、解析(parser.cpp)、求值(eval.cpp/expand.cpp)、扩展合并(extend.cpp)、CSS 输出(output.cpp/emitter.cpp)、C API 绑定(sass_context.cpp/to_c.cpp/to_value.cpp)等。该文件头部注释还解释了源文件排列顺序的用意:编译耗时大的文件放在前面,避免它们成为并行编译的最后单元,同时交错排布以缓解内存峰值。
编译宏与版本号的注入
Makefile 会自动把版本号编译进库:
ifeq ($(LIBSASS_VERSION),) 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 endif ... ifeq ($(LIBSASS_VERSION),) ifeq (,$(LIBSASS_VERSION),) CFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\""优先级为:环境变量LIBSASS_VERSION> git describe >VERSION文件,与 version.sh 的逻辑一致;编译出的字符串最终体现在 include/sass/version.h 的LIBSASS_VERSION宏中(缺省为"[NA]")。这也是 build.md 中"Including the LibSass version"一节所说的g++ -DLIBSASS_VERSION="\"x.y.z\""机制在 makefile 构建中的自动化版本。
此外,若设置了COVERAGE变量,优化级别会从-O2降为-O1 -fno-omit-frame-pointer,为覆盖率统计保留栈帧(Makefile);EXTRA_CFLAGS/EXTRA_CXXFLAGS/EXTRA_LDFLAGS可作为额外编译选项注入(Makefile)。
安装到系统
原文档的推荐意见是:安装到系统级目录时优先走 autotools 流程(参见 build-with-autotools.md),因为 libtool 会带来更好的动态库管理。若坚持用 makefile 安装,前提是系统装有 GNUinstall工具(或兼容实现):
yum install coreutils # RedHat Linux emerge -a coreutils # Gentoo Linux pkgin install coreutils # SmartOS安装位置通过PREFIX控制:
PREFIX="/opt/local" make install对应 Makefile 中的行为(Makefile):
ifeq (,$(TRAVIS_BUILD_DIR)) ifeq ($(OS),SunOS) PREFIX ?= /opt/local else PREFIX ?= /usr/local endif else PREFIX ?= $(TRAVIS_BUILD_DIR) endif即PREFIX默认在 Solaris/Illumos 上是/opt/local,其余平台是/usr/local,且在 CI(Travis)环境里指向构建目录。安装目标按构建类型分派(Makefile):
make install-static:只安装lib/libsass.a到$(PREFIX)/lib/;make install-shared:安装lib/libsass.so,并通过install-headers把公开头文件拷贝到$(PREFIX)/include/(包括sass.h、sass2scss.h以及include/sass/下的base.h、version.h、values.h、context.h、functions.h)。
所有拷贝都通过 GNUinstall完成,例如头文件用install -v -m0644,库文件用install -v -m0755;Makefile 顶部还注意到 Solaris 上该工具名为ginstall(coreutils 版本),故在OS=SunOS时切换INSTALL变量。
编译 sassc 命令行工具
sassc 是基于 libsass C API 的轻量 CLI,spec 测试也依赖它来驱动。原文档的操作:
# Let build know library location export SASS_LIBSASS_PATH="`pwd`/libsass" # Invokes the sassc makefile make -C libsass -j5 sassc在 Makefile 中的实现是:
$(SASSC_BIN): $(BUILD) $(MAKE) -C $(SASS_SASSC_PATH) build-$(BUILD)-dev sassc: $(SASSC_BIN) $(SASSC_BIN) -v version: $(SASSC_BIN) $(SASSC_BIN) -h $(SASSC_BIN) -v可以看到几个要点:
- sassc 的构建依赖
$(BUILD)目标,即先确保 libsass 本体(static 或 shared)已编译完成,再进入sassc/目录执行其自身的build-$(BUILD)-dev目标——这就是为什么SASS_LIBSASS_PATH要指向 libsass 目录,sassc 的 Makefile 用它来定位lib/下的库文件; make sassc成功后会自动执行一次sassc -v打印版本作为验证;make version则同时打印帮助信息和版本号;- Windows 下产物名是
sassc.exe(Makefile)。
需要说明:node-sass 仓库本身不依赖 sassc,它通过 src/binding.cpp 与 src/libsass.gyp 直接以 gyp 构建 libsass 源码并链接进 Node 原生模块;sassc 只在你想手动验证 libsass CLI 行为、或跑 spec 测试时才需要。
运行 sass-spec 规范测试套件
# needs ruby available # also gem install minitest make -C libsass -j5 test_build该目标的前提条件(Makefile):
test: $(SASSC_BIN) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) test_build: $(SASSC_BIN) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) test_full: $(SASSC_BIN) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass --run-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) test_probe: $(SASSC_BIN) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass --probe-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)从源码结构看:
test_build(以及别名test)会先构建 sassc,然后用 Ruby 运行sass-spec/sass-spec.rb,指定 Sass 语言版本-V 3.5、被测命令为刚编出的sassc、实现标记为--impl libsass,测试目录为sass-spec/spec/;test_full额外带--run-todo,把标记为"已知未通过"(LibSass-todo-issues)的用例也纳入执行;test_probe带--probe-todo,用于探测哪些 todo 用例现在其实已经能通过;- 依赖链是
test_build → sassc → $(BUILD) → libsass.a/libsass.so,因此直接make test_build即可从零完成"编库 → 编 sassc → 跑 spec"的完整流程。这与 autotools 构建中 GNUmakefile.am 定义的test/test_build/test_full/test_probe目标在参数上保持一致,只是 makefile 路线用外部克隆的sassc目录,autotools 路线则把sassc.c直接编进树内tester可执行文件。
build.md 补充了运行前提:需要可用的 Ruby(建议 2.1+,1.9 在 Windows 上有问题)以及minitestgem:
ruby -v gem install minitest # should be optional gem install minitap与 autotools 路线的取舍
两条构建路线在 node-sass 内嵌的 libsass 树中是并存的,取舍建议如下:
| 场景 | 推荐路线 | 原因 |
|---|---|---|
只想快速得到libsass.a/libsass.so做二次开发 | makefile | 无额外依赖,make -j一条命令 |
| 把 libsass 作为系统动态库安装 | autotools | libtool 管理.so版本与 rpath,--prefix语义标准 |
| 在 Makefile 安装后仍需系统级头文件/库 | PREFIX=... make install-shared | 会同时安装include/下全部公开头文件 |
makefile 路线还有一个实用技巧:它暴露了lib-file/lib-opts查询目标(Makefile),便于在自己的构建脚本中探测库路径:
# 打印库文件位置,如 /path/to/libsass/lib/libsass.a make -C libsass lib-file-shared # 打印链接参数,如 -L/path/to/libsass/lib -lsass make -C libsass lib-opts-shared小结
围绕 build-with-makefiles.md 描述的构建流程,配合 Makefile、Makefile.conf 的源码可以实现一条完整的本地构建链:克隆三仓库 → 用BUILD变量决定 static/shared →make -j5产出lib/libsass.a/lib/libsass.so→ 可选PREFIX=... make install安装系统库 →make sassc编译 CLI 并自动打印版本 →make test_build通过 sass-spec 回归验证实现。对于维护 node-sass 这类 libsass 上层绑定的项目,这条链既是排查原生编译失败的基线对照,也是验证底层 C++ 改动是否破坏 Sass 语义的标准测试手段。
- 前端
- 构建工具
【免费下载链接】node-sass
:rainbow: Node.js bindings to libsass
相关推荐
node-sass 底层解析:LibSass C 上下文 API(Sass Context)全解
node sass 底层解析:LibSass C 上下文 API(Sass Context)全解 本文以 LibSass 的 C 上下文接口文档 api con
前端构建工具用 Autotools 构建并安装 libsass 系统库:node-sass 内嵌 libsass 的构建全流程详解
用 Autotools 构建并安装 libsass 系统库:node sass 内嵌 libsass 的构建全流程详解 本篇指南以 node sass 仓库中随
前端构建工具node-sass 与 libsass 的 Context API 内部结构剖析:从 C 结构体到编译器状态机
node sass 与 libsass 的 Context API 内部结构剖析:从 C 结构体到编译器状态机 本文以 libsass 的内部设计文档 api
前端构建工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考