diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 04a1a9f79..d4f5c7b4e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -131,6 +131,69 @@ jobs: run: | make docker-test-dist + build-macos: + name: Build macOS + runs-on: macos-latest + env: + CFLAGS: "-I/opt/homebrew/include/luajit-2.1 -I/opt/homebrew/include" + OBJCFLAGS: "-I/opt/homebrew/include/luajit-2.1 -I/opt/homebrew/include" + LDFLAGS: "-L/opt/homebrew/lib" + DOCKER: false + LDOC: false + NIX: false + SHA256SUM: false + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Cache test fonts + uses: actions/cache@v4 + with: + path: | + .fonts + .sources + key: fonts-${{ hashFiles('Makefile-fonts') }} + - name: Cache lua_modules + uses: actions/cache@v4 + with: + path: | + lua_modules + key: luarocks-${{ hashFiles('Makefile-luarocks', 'sile.rockspec.in') }} + - name: Install system dependencies + run: | + brew install \ + autoconf \ + automake \ + expat \ + ghostscript \ + graphviz \ + libtool \ + luajit \ + luarocks \ + poppler \ + rust \ + unzip \ + zlib + brew link icu4c@76 --force + brew link zlib --force + brew link expat --force + brew install --cask font-gentium-plus + - name: Configure + run: | + ./bootstrap.sh + ./configure \ + --enable-developer-mode \ + --without-developer-tools \ + --with-system-lua-sources \ + --with-manual + echo "VERSION=$(./build-aux/git-version-gen .tarball-version)" >> $GITHUB_ENV + # Note don't use -Otarget for macOS, Homebrew's old make is too buggy + echo "MAKEFLAGS=-j$(sysctl -n hw.ncpu)" >> $GITHUB_ENV + - name: Make + run: | + make + build-nix: runs-on: ubuntu-22.04 name: Build Nix diff --git a/Makefile.am b/Makefile.am index c43a302d8..19ba5b72f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -56,7 +56,6 @@ endif $(MANUAL): $(FIGURES) BUILT_SOURCES_LUA = core/features.lua core/pathsetup.lua core/version.lua -RUSILE_SOURCES = rusile/Cargo.toml rusile/src/lib.rs bin_PROGRAMS = sile bin_SCRIPTS = sile-lua @@ -67,8 +66,6 @@ EXTRA_sile_SOURCES = if !EMBEDDED_RESOURCES nobase_dist_pkgdata_DATA = $(SILEDATA) $(LUALIBRARIES) nobase_nodist_pkgdata_DATA = $(BUILT_SOURCES_LUA) $(LUAMODULES) -pkglib_LIBRARIES = rusile.so -rusile_so_SOURCES = $(RUSILE_SOURCES) endif !EMBEDDED_RESOURCES dist_doc_DATA = README.md CHANGELOG.md dist_pdf_DATA = $(_MANUAL) @@ -137,7 +134,7 @@ $(CARGO_BIN): justenough/.libs/justenoughlibtexpdf.a $(CARGO_BIN): justenough/.libs/svg.a $(CARGO_BIN): libtexpdf/.libs/libtexpdf.a if !EMBEDDED_RESOURCES -$(CARGO_BIN): rusile.so +$(CARGO_BIN): rusile.$(SHARED_LIB_EXT) endif !EMBEDDED_RESOURCES src/embed-includes.rs: Makefile-distfiles @@ -164,7 +161,7 @@ else MLUAVER = lua$(LUA_SHORT_VERSION) endif CARGO_FEATURE_ARGS = --features $(MLUAVER) -RUSILE_FEATURE_ARG = --features $(MLUAVER) +rusile_FEATURE_ARGS = --features $(MLUAVER) if !SYSTEM_LUA_SOURCES CARGO_FEATURE_ARGS += --features vendored @@ -178,10 +175,6 @@ if FONT_VARIATIONS CARGO_FEATURE_ARGS += --features variations endif -rusile.so: $(rusile_so_SOURCES) $(bin_PROGRAMS) - $(CARGO_ENV) $(CARGO) build $(CARGO_VERBOSE) --target $(CARGO_TARGET_TRIPLE) $(RUSILE_FEATURE_ARG) $(CARGO_RELEASE_ARGS) -p rusile - $(INSTALL) @builddir@/target/@RUST_TARGET_SUBDIR@/lib$@ $@ - DEPDIR := .deps LOCALFONTS := FONTCONFIG_FILE=$(PWD)/fontconfig.conf LOCALPATHS := SILE_PATH="$(PWD);libtexpdf/.libs;justenough/.libs" diff --git a/build-aux/module.rs b/build-aux/module.rs new file mode 100644 index 000000000..e10b0cdcd --- /dev/null +++ b/build-aux/module.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-arg=-Wl,-undefined,dynamic_lookup"); +} diff --git a/build-aux/que_rust_boilerplate.m4 b/build-aux/que_rust_boilerplate.m4 index bc3d5ffaf..050afbf4e 100644 --- a/build-aux/que_rust_boilerplate.m4 +++ b/build-aux/que_rust_boilerplate.m4 @@ -44,3 +44,14 @@ $($SED -E "s/@PACKAGE_VAR@/$PACKAGE_VAR/g;s/@PACKAGE_NAME@/$PACKAGE_NAME/g" buil ])dnl ]) + +AC_DEFUN([QUE_RUST_MODULE], [ + + AC_REQUIRE([AX_AM_MACROS]) + AX_ADD_AM_MACRO([dnl +EXTRA_DIST += build-aux/que_rust_module.am + +$($SED -E "s/@MODULE@/$1/g;s/@SHARED_LIB_EXT@/$SHARED_LIB_EXT/g" build-aux/que_rust_module.am) +])dnl + +]) diff --git a/build-aux/que_rust_module.am b/build-aux/que_rust_module.am new file mode 100644 index 000000000..708b6a12b --- /dev/null +++ b/build-aux/que_rust_module.am @@ -0,0 +1,8 @@ +pkglib_LIBRARIES = @MODULE@.@SHARED_LIB_EXT@ +@MODULE@_@SHARED_LIB_EXT@_SOURCES = @MODULE@/Cargo.toml @MODULE@/src/lib.rs build-aux/module.rs + +@builddir@/target/@RUST_TARGET_SUBDIR@/lib@MODULE@.@SHARED_LIB_EXT@: $(@MODULE@_@SHARED_LIB_EXT@_SOURCES) + $(CARGO_ENV) $(CARGO) build $(CARGO_VERBOSE) --target $(CARGO_TARGET_TRIPLE) $(@MODULE@_FEATURE_ARGS) $(CARGO_RELEASE_ARGS) -p @MODULE@ + +@MODULE@.@SHARED_LIB_EXT@: @builddir@/target/@RUST_TARGET_SUBDIR@/lib@MODULE@.@SHARED_LIB_EXT@ + $(INSTALL) $< $@ diff --git a/configure.ac b/configure.ac index ac639e639..bd55b7a09 100644 --- a/configure.ac +++ b/configure.ac @@ -2,6 +2,7 @@ AC_PREREQ([2.69]) AC_INIT([sile], [m4_esyscmd(build-aux/git-version-gen .tarball-version)], [caleb@alerque.com]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([build-aux]) +AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([foreign tar-pax dist-zstd dist-zip no-dist-gzip color-tests subdir-objects]) AM_SILENT_RULES([yes]) @@ -37,11 +38,24 @@ RANLIB=: LT_PREREQ([2.2]) LT_INIT([dlopen]) -AC_CANONICAL_HOST - QUE_RUST_BOILERPLATE QUE_DOCKER_BOILERPLATE +# Load Rust module bits, early since it stuffs rules in aminclude.am +case $target_os in + darwin*) + SHARED_LIB_EXT="dylib" + ;; + cygwin*|mingw*) + SHARED_LIB_EXT="dll" + ;; + *) + SHARED_LIB_EXT="so" + ;; +esac +AC_SUBST([SHARED_LIB_EXT]) +QUE_RUST_MODULE([rusile]) + AM_CONDITIONAL([SHARED], [test "x$enable_shared" = "xyes"]) AM_CONDITIONAL([STATIC], [test "x$enable_static" = "xyes"]) @@ -111,7 +125,7 @@ AM_COND_IF([MANUAL], [ AC_MSG_CHECKING([for OS X]) have_appkit=no -case $host_os in +case $target_os in darwin*) AC_MSG_RESULT([yes]) AC_MSG_CHECKING([for AppKit works]) @@ -244,20 +258,6 @@ AC_SUBST([HARFBUZZ_SUBSET_LIBS]) AC_SUBST([ICU_TRUE]) AC_SUBST([ICU_CFLAGS]) AC_SUBST([ICU_LIBS]) - -case $host_os in - darwin*) - LUAROCKSARGS="EXPAT_DIR=/usr/local/opt/expat OPENSSL_DIR=/usr/local/opt/openssl ZLIB_DIR=/usr/local/opt/zlib" - SHARED_LIB_EXT="so" - ;; - cygwin*|mingw*) - SHARED_LIB_EXT="dll" - ;; - *) - SHARED_LIB_EXT="so" - ;; -esac -AC_SUBST([SHARED_LIB_EXT]) AC_SUBST([LUAROCKSARGS]) # Avoid need for `--datarootdir=$(cd ..; pwd)` hack to run locally for diff --git a/core/cli.lua b/core/cli.lua index f5a2135d3..d9dbced06 100644 --- a/core/cli.lua +++ b/core/cli.lua @@ -73,7 +73,7 @@ cli.parseArguments = function () SILE.input.filenames = opts.INPUTS end if opts.backend then - SILE.backend = opts.backend + SILE.input.backend = opts.backend end if opts.class then SILE.input.class = opts.class @@ -90,7 +90,7 @@ cli.parseArguments = function () table.insert(SILE.input.evaluateAfters, statement) end if opts.fontmanager then - SILE.forceFontManager = opts.fontmanager + SILE.input.fontmanager = opts.fontmanager end if opts.makedeps then SILE.makeDeps = require("core.makedeps") diff --git a/core/fontmanager.lua b/core/fontmanager.lua index 44792dc97..b541e6dd4 100644 --- a/core/fontmanager.lua +++ b/core/fontmanager.lua @@ -1,20 +1,36 @@ local fontManager = {} + fontManager.fontconfig = require("justenoughfontconfig") -pcall(function () - fontManager.macfonts = require("macfonts") -end) + +local has_macfonts, macfonts = pcall(require, "macfonts") +if has_macfonts and macfonts then + fontManager.macfonts = macfonts +end + +local function create_macfonts_fallback (self) + return function (...) + SU.debug("fonts", "Checking via macfonts") + local status, result = pcall(self.macfonts._face, ...) + if status and result and result.filename then + SU.debug("fonts", "Found, returning result") + return result + else + SU.debug("fonts", "Not found, trying fontconfig instead") + return self.fontconfig._face(...) + end + end +end fontManager.face = function (self, ...) - local manager - if SILE.forceFontManager then - manager = self[SILE.forceFontManager] + local face + if SILE.input.fontmanager then + face = self[SILE.input.fontmanager]._face + elseif has_macfonts then + face = create_macfonts_fallback(self) else - manager = self.macfonts and self.macfonts or self.fontconfig - end - if not manager then - SU.error("Failed to load any working font manager") + face = self.fontconfig._face end - return manager._face(...) + return face(...) end return fontManager diff --git a/core/pathsetup.lua.in b/core/pathsetup.lua.in index d7556f58d..3ad11b287 100644 --- a/core/pathsetup.lua.in +++ b/core/pathsetup.lua.in @@ -43,6 +43,9 @@ end -- Prepend paths specifically for C modules. local function prependCPath (path) package.cpath = prepend_and_dedup(path .. "/?.@SHARED_LIB_EXT@", package.cpath) + if "@SHARED_LIB_EXT@" ~= "so" then + package.cpath = prepend_and_dedup(path .. "/?.so", package.cpath) + end end -- Take a given path and iterate over permutations of paths that LuaRocks might have installed a rock to that are diff --git a/core/sile.lua b/core/sile.lua index 1192baf6a..4881b16f4 100644 --- a/core/sile.lua +++ b/core/sile.lua @@ -205,22 +205,26 @@ end -- -- Does not move on to processing input document(s). function SILE.init () - if not SILE.backend then - SILE.backend = "libtexpdf" + if SILE.backend then + SU.deprecated("SILE.backend", "SILE.input.backend", "0.15.7", "0.17.0") + SILE.input.backend = SILE.backend end - if SILE.backend == "libtexpdf" then + if not SILE.input.backend then + SILE.input.backend = "libtexpdf" + end + if SILE.input.backend == "libtexpdf" then SILE.shaper = SILE.shapers.harfbuzz() SILE.outputter = SILE.outputters.libtexpdf() - elseif SILE.backend == "cairo" then + elseif SILE.input.backend == "cairo" then SILE.shaper = SILE.shapers.pango() SILE.outputter = SILE.outputters.cairo() - elseif SILE.backend == "debug" then + elseif SILE.input.backend == "debug" then SILE.shaper = SILE.shapers.harfbuzz() SILE.outputter = SILE.outputters.debug() - elseif SILE.backend == "text" then + elseif SILE.input.backend == "text" then SILE.shaper = SILE.shapers.harfbuzz() SILE.outputter = SILE.outputters.text() - elseif SILE.backend == "dummy" then + elseif SILE.input.backend == "dummy" then SILE.shaper = SILE.shapers.harfbuzz() SILE.outputter = SILE.outputters.dummy() end diff --git a/inputters/lua_spec.lua b/inputters/lua_spec.lua index 2b9f2892d..588e512ff 100644 --- a/inputters/lua_spec.lua +++ b/inputters/lua_spec.lua @@ -1,5 +1,5 @@ SILE = require("core.sile") -SILE.backend = "dummy" +SILE.input.backend = "dummy" SILE.init() SILE.utilities.error = error diff --git a/inputters/sil_spec.lua b/inputters/sil_spec.lua index 78fdf4785..69290a0f9 100644 --- a/inputters/sil_spec.lua +++ b/inputters/sil_spec.lua @@ -1,5 +1,5 @@ SILE = require("core.sile") -SILE.backend = "dummy" +SILE.input.backend = "dummy" SILE.init() SILE.utilities.error = error diff --git a/inputters/xml_spec.lua b/inputters/xml_spec.lua index 3b2077e41..d983251a2 100644 --- a/inputters/xml_spec.lua +++ b/inputters/xml_spec.lua @@ -1,5 +1,5 @@ SILE = require("core.sile") -SILE.backend = "dummy" +SILE.input.backend = "dummy" SILE.init() SILE.utilities.error = error diff --git a/packages/counters/counters_spec.lua b/packages/counters/counters_spec.lua index 74e53d65a..14d760829 100644 --- a/packages/counters/counters_spec.lua +++ b/packages/counters/counters_spec.lua @@ -1,5 +1,5 @@ SILE = require("core.sile") -SILE.backend = "dummy" +SILE.input.backend = "dummy" SILE.init() SILE.utilities.error = error diff --git a/rusile/Cargo.toml b/rusile/Cargo.toml index dbe411618..3f108ec59 100644 --- a/rusile/Cargo.toml +++ b/rusile/Cargo.toml @@ -9,6 +9,7 @@ authors.workspace = true homepage.workspace = true repository.workspace = true license.workspace = true +build = "../build-aux/module.rs" [lib] crate-type = ["rlib", "cdylib", "staticlib"] diff --git a/spec/complex_frame_spec.lua b/spec/complex_frame_spec.lua index b20db4af2..7bc2c00ac 100644 --- a/spec/complex_frame_spec.lua +++ b/spec/complex_frame_spec.lua @@ -1,6 +1,6 @@ SILE = require("core.sile") -SILE.backend = "dummy" +SILE.input.backend = "dummy" SILE.init() local base = require("classes.base") diff --git a/spec/fontmanager_spec.lua b/spec/fontmanager_spec.lua new file mode 100644 index 000000000..e6b822afb --- /dev/null +++ b/spec/fontmanager_spec.lua @@ -0,0 +1,54 @@ +SILE = require("core.sile") +SILE.input.backend = "debug" +SILE.init() + +-- These tests depend on loading specific fonts from our test fixtures. Running +-- plain `busted` is sometimes useful (e.g. for IDEs) but will not support this +-- test because fontconfig hasn't been preloaded with the font paths it would +-- need. If that's the case, just skip even defining these tests and call it +-- good. To test a complete set of tests use `make busted`. +local fcf = os.getenv("FONTCONFIG_FILE") +if not fcf then + return +end + +describe("The fontconfig manager", function () + SILE.input.fontmanager = "fontconfig" + + it("should load a font", function () + local family = "Libertinus Serif" + local face = SILE.shaper.getFace({ family = family }) + assert.is.equal(family, face.family) + end) + + it("should fallback when it can't find", function () + local family = "Yesteryear Imagination" + local face + assert.has_no.errors(function () + face = SILE.shaper.getFace({ family = family }) + end) + assert.is_not.equal(family, face.family) + end) +end) + +describe("The macfonts manager", function () + + if not SILE.fontManager.macfonts then + return + end + + it("should load a font", function () + local family = "Libertinus Serif" + local face = SILE.shaper.getFace({ family = family }) + assert.is.equal(family, face.family) + end) + + it("should fallback to fontconfig when it fails", function () + local family = "Yesteryear Imagination" + local face + assert.has_no.errors(function () + face = SILE.shaper.getFace({ family = family }) + end) + assert.is_not.equal(family, face.family) + end) +end) diff --git a/spec/hyphenator_spec.lua b/spec/hyphenator_spec.lua index 5daa07831..4363dc2d0 100644 --- a/spec/hyphenator_spec.lua +++ b/spec/hyphenator_spec.lua @@ -1,6 +1,6 @@ SILE = require("core.sile") -- Using French below requires the shaper to be initialized -SILE.backend = "debug" +SILE.input.backend = "debug" SILE.init() describe("Hyphenation module", function () diff --git a/spec/opentype_spec.lua b/spec/opentype_spec.lua index 82c4342ed..fcf183d5e 100644 --- a/spec/opentype_spec.lua +++ b/spec/opentype_spec.lua @@ -1,6 +1,6 @@ SILE = require("core.sile") -SILE.backend = "debug" -SILE.forceFontManager = "fontconfig" +SILE.input.backend = "debug" +SILE.input.fontmanager = "fontconfig" SILE.init() -- These tests depend on loading specific fonts from our test fixtures. Running diff --git a/spec/shaper_spec.lua b/spec/shaper_spec.lua index 3a548a2db..72f600f9e 100644 --- a/spec/shaper_spec.lua +++ b/spec/shaper_spec.lua @@ -1,5 +1,5 @@ SILE = require("core.sile") -SILE.backend = "debug" +SILE.input.backend = "debug" SILE.init() describe("SILE.shapers.base", function () diff --git a/src/lib.rs b/src/lib.rs index 00cf621be..6bf4b8195 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,10 +133,10 @@ pub fn run( sile_input.set("evaluateAfters", expressions)?; } if let Some(backend) = backend { - sile.set("backend", backend)?; + sile_input.set("backend", backend)?; } if let Some(fontmanager) = fontmanager { - sile.set("fontmanager", fontmanager)?; + sile_input.set("fontmanager", fontmanager)?; } if let Some(class) = class { sile_input.set("class", class)?;