From 41a2dded3651cd131b21096dd9d14bab685ac5e3 Mon Sep 17 00:00:00 2001 From: Michael Raitza Date: Fri, 9 Feb 2024 18:36:08 +0100 Subject: [PATCH] factor: Restructure package for easier extension --- doc/languages-frameworks/factor.section.md | 136 +++++++++++ doc/languages-frameworks/index.md | 1 + .../manual/release-notes/rl-2411.section.md | 2 + .../adjust-paths-in-unit-tests.patch | 33 +-- .../compilers/factor-lang/factor99.nix | 224 ------------------ .../factor-lang/ld.so.cache-from-env.patch | 27 +++ .../factor-lang/mk-factor-application.nix | 130 ++++++++++ .../compilers/factor-lang/scope.nix | 28 ++- .../compilers/factor-lang/unwrapped.nix | 110 +++++++++ .../compilers/factor-lang/wrapper.nix | 165 +++++++++++++ pkgs/top-level/all-packages.nix | 2 +- 11 files changed, 606 insertions(+), 252 deletions(-) create mode 100644 doc/languages-frameworks/factor.section.md delete mode 100644 pkgs/development/compilers/factor-lang/factor99.nix create mode 100644 pkgs/development/compilers/factor-lang/ld.so.cache-from-env.patch create mode 100644 pkgs/development/compilers/factor-lang/mk-factor-application.nix create mode 100644 pkgs/development/compilers/factor-lang/unwrapped.nix create mode 100644 pkgs/development/compilers/factor-lang/wrapper.nix diff --git a/doc/languages-frameworks/factor.section.md b/doc/languages-frameworks/factor.section.md new file mode 100644 index 0000000000000..d8297d4191a77 --- /dev/null +++ b/doc/languages-frameworks/factor.section.md @@ -0,0 +1,136 @@ +# Factor {#sec-language-factor} + +## Development Environment {#ssec-dev-env} + +All Nix expressions for the Factor compiler and development environment can be found in `pkgs/development/compilers/factor-lang/scope.nix`. + +The default package `factor-lang` provides support for the built-in graphical user interface and a selected set of C library bindings, e.g., for sound and TLS connections. +It also comes with the Fuel library for Emacs that provides an integrated development environment for developing Factor programs including access to the Factor runtime and online documentation. + +For using less frequently used libraries that need additional bindings, you can override the `factor-lang` package and add more library bindings and/or binaries to its PATH. +The package is defined in `pkgs/development/compilers/factor-lang/wrapper.nix` and provides several attributes for adding those: + +- `runtimeLibs` adds the packages' `/lib` paths to the wrapper and adds all shared libraries to an ld.so cache such that they can be found dynamically by the Factor runtime. +- `binPackages` does the same as `runtimeLibs` and additionally adds the packages to Factor's PATH environment variable. +- `extraVocabs` adds Factor vocabularies to the tree that are not part of the standard library. + The packages must adhere to the default vocabulary root structure to be found. +- `guiSupport` draws in all necessary graphical libraries to enable the Factor GUI. + This should be set to `true` when considering building and running graphical applications with this Factor runtime (even if the Factor GUI is not used for programming). + This argument is `true` by default. +- `enableDefaults` can be deactivated to only wrap libraries that are named in `runtimeLibs` or `binPackages`. + This reduces the runtime dependencies especially when shipping Factor applications. + +The package also passes through several attributes listing the wrapped libraries and binaries, namely, `runtimeLibs` and `binPackages` as well as `defaultLibs` and `defaultBins`. + +`factor-lange-scope` provides pre-configured Factor packages: +- `factor-lang-scope.factor-lang` is the default package with GUI support and several default library bindings (e.g. openssl, openal etc.). +- `factor-lang-scope.factor-no-gui` turns off GUI support while maintaining default library bindings. +- `factor-lang-scope.factor-minimal` comes with practically no additional library bindings and binaries and no GUI support. + +### Scaffolding and the `work` vocabulary root {#ssec-scaffolding} + +Factor uses the concept of "scaffolding" to spin off a new vocabulary in a personal workspace rooted at the `work` vocabulary root. +This concept does not scale very well, because it makes many assumptions which all turn out to be wrong at some point. +In the current implementation, the `work` vocabulary root points to `/var/lib/factor` on the target machine. +This can be suitable for a single-user system. +Create the location and make it writable to your user. +Then, you can use the `scaffold-work` word as instructed by many tutorials. + +If you don't like this approach, you can work around it by creating a `~/.factor-roots` file in your home directory which contains the locations you desire to represent additional Factor vocabulary roots, one directory per line. +Use `scaffold-vocab` to create your vocabularies in one of these additional roots. +The online Factor documentation is extensive on how to use the scaffolding framework. + +## Packaging Factor Vocabularies {#ssec-packaging} + +All Factor vocabularies that shall be added to a Factor environment via the `extraVocabs` attribute must adhere to the following directory scheme. +Its top-level directory must be one (or multiple) of `basis`, `core` or `extra`. +`work` is routed to `/var/lib/factor` and is not shipped nor referenced in the nix store, see the section on [scaffolding](#ssec-scaffolding). +You should usually use `extra`, but you can use the other roots to overwrite built-in vocabularies. +Be aware that vocabularies in `core` are part of the Factor image which the development environment is run from. +This means the code in those vocabularies is not loaded from the sources, such that you need to call `refresh-all` to re-compile and load the changed definitions. +In these instances, it is advised to override the `factor-unwrapped` package directly, which compiles and packages the core Factor libraries into the default Factor +image. + +As per Factor convention, your vocabulary `foo.factor` must be in a directory of the same name in addition to one of the previously mentioned vocabulary roots, e.g. `extra/foo/foo.factor`. + +All extra Factor vocabularies are registered in `pkgs/development/compilers/factor-lang/scope.nix`. + + +## Building Applications {#ssec-applications} + +Factor applications are built using Factor's `deploy` facility with the help of the `buildFactorApplication` function. + +### `buildFactorApplication` function {#ssec-buildFactorApplication-func} + +When packaging a Factor application with [`buildFactorApplication`](#ssec-buildFactorApplication-func), its [`override`](#sec-pkg-override) interface should contain the `factor-lang-scope` argument. +For example: +```nix +{ lib, fetchurl, factor-lang-scope }: + +factor-lang-scope.buildFactorApplication (finalAttrs: { + pname = "foo"; + version = "1.0"; + + src = fetchurl { + url = "https://some-forge.org/foo-${finalAttrs.version}.tar.gz" + }; +}) +``` + +The `buildFactorApplication` function expects the following source structure for a package `foo-1.0` and produces a `/bin/foo` application: +``` +foo-1.0/ + foo/ + foo.factor + deploy.factor + ... +``` + +It provides the additional attributes `vocabName` and `binName` to cope with naming deviations. +The `deploy.factor` file controls how the application is deployed and is documented in the Factor online documentation on the `deploy` facility. + +Use the `preInstall` or `postInstall` hooks to copy additional files and directories to `out/`. +The function itself only builds the application in `/lib/factor/` and a wrapper in `/bin/`. + +A more complex example shows how to specify runtime dependencies for the wrapped application: +```nix +{ lib, fetchurl, factor-lang-scope, curl }: + +factor-lang-scope.buildFactorApplication (finalAttrs: { + pname = "foo"; + version = "1.0"; + + src = fetchurl { + url = "https://some-forge.org/foo-${finalAttrs.version}.tar.gz"; + hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + + extraPaths = with finalAttrs.factor-lang; binPackages ++ defaultBins ++ [ curl ]; +}) +``` + +It requires the packager to specify the full set of binaries to be made available at runtime. +This enables the standard pattern for application packages to specify all runtime dependencies explicitly without the Factor runtime interfering. + +Additional attribute that are understood by `buildFactorApplication`: +- `vocabName` is the path to the vocabulary to be deployed relative to the source root. + So, directory `foo/` from the example above could be `extra/deep/down/foo`. + This allows you to maintain Factor's vocabulary hierarchy and distribute the same source tree as a stand-alone application and as a library in the Factor development environment via the `extraVocabs` attribute. +- `binName` is the name of the resulting binary in `/bin/`. + It defaults to the last directory component in `vocabName`. + It is also added as the `meta.mainProgram` attribute to facilitate `nix run`. +- `enableUI` is `false` by default. + Set this to `true` when you ship a graphical application. +- `extraLibs` adds additional libraries as runtime dependencies. + Defaults to the `defaultLibs ++ runtimeLibs` passthru attributes from the used factor-lang package. + Setting this disables the defaults. + Thus, all necessary libraries must be listed. +- `extraPaths` adds additional binaries to the runtime PATH environment variable (without adding their libraries, as well). + Defaults to the `defaultBins ++ binPackages` attributes from the used factor-lang package. + Setting this disables the defaults. + Thus, all necessary binary packages must be listed. +- `deployScriptText` is the actual deploy Factor file that is executed to deploy the application. + You can change it if you need to perform additional computation during deployment. +- `factor-lang` overrides the Factor package to use to deploy this application, which also affects the default library bindings and programs in the runtime PATH. + It defaults to `factor-lang` when `enableUI` is turned on and `factor-no-gui` when it is turned off. + Applications that use only Factor libraries without external bindings or programs may set this to `factor-minimal`. diff --git a/doc/languages-frameworks/index.md b/doc/languages-frameworks/index.md index 021f4807fb226..5a0c7c0a393d0 100644 --- a/doc/languages-frameworks/index.md +++ b/doc/languages-frameworks/index.md @@ -65,6 +65,7 @@ dhall.section.md dlang.section.md dotnet.section.md emscripten.section.md +factor.section.md gnome.section.md go.section.md hare.section.md diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 256a0fd255988..2a0119b9f2787 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -17,6 +17,8 @@ - `compressDrv` can compress selected files in a derivation. `compressDrvWeb` compresses files for common web server usage (`.gz` with `zopfli`, `.br` with `brotli`). +- The Factor programming language packages were reworked and `factor-lang-scope` now provides a `buildFactorApplication` function to deploy Factor programs as binaries. It has also received proper documentation in the Nixpkgs manual. + - `hardware.display` is a new module implementing workarounds for misbehaving monitors through setting up custom EDID files and forcing kernel/framebuffer modes. diff --git a/pkgs/development/compilers/factor-lang/adjust-paths-in-unit-tests.patch b/pkgs/development/compilers/factor-lang/adjust-paths-in-unit-tests.patch index 10f812e3814a8..f0374b80b8c03 100644 --- a/pkgs/development/compilers/factor-lang/adjust-paths-in-unit-tests.patch +++ b/pkgs/development/compilers/factor-lang/adjust-paths-in-unit-tests.patch @@ -1,24 +1,19 @@ -From da8a4b9c1094a568f443c525ca1ce11f686be1bc Mon Sep 17 00:00:00 2001 -From: timor -Date: Thu, 8 Aug 2019 14:13:09 +0200 -Subject: [PATCH] adjust unit test for finding executables in path for NixOS - ---- - basis/io/standard-paths/unix/unix-tests.factor | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/basis/io/standard-paths/unix/unix-tests.factor b/basis/io/standard-paths/unix/unix-tests.factor -index acd5029..870537f 100644 ---- a/basis/io/standard-paths/unix/unix-tests.factor -+++ b/basis/io/standard-paths/unix/unix-tests.factor -@@ -5,13 +5,13 @@ sequences tools.test ; - +diff -ur factor.orig/basis/io/standard-paths/unix/unix-tests.factor factor/basis/io/standard-paths/unix/unix-tests.factor +--- factor.orig/basis/io/standard-paths/unix/unix-tests.factor 2024-02-09 14:38:33.932439180 +0100 ++++ factor/basis/io/standard-paths/unix/unix-tests.factor 2024-02-09 15:41:18.529141569 +0100 +@@ -1,21 +1,21 @@ + ! Copyright (C) 2011 Doug Coleman. + ! See https://factorcode.org/license.txt for BSD license. + USING: environment io.standard-paths io.standard-paths.unix +-sequences tools.test ; ++kernel sequences tools.test ; + { f } [ "" find-in-path ] unit-test { t } [ - "ls" find-in-path { "/bin/ls" "/usr/bin/ls" } member? + "ls" find-in-path not not ] unit-test - + { t } [ "/sbin:" "PATH" os-env append "PATH" [ "ps" find-in-path @@ -26,3 +21,9 @@ index acd5029..870537f 100644 + not not ] with-os-env ] unit-test + + { t } [ + "ls" find-in-standard-login-path +- { "/bin/ls" "/usr/bin/ls" } member? ++ not not + ] unit-test diff --git a/pkgs/development/compilers/factor-lang/factor99.nix b/pkgs/development/compilers/factor-lang/factor99.nix deleted file mode 100644 index a020d6a2ce31e..0000000000000 --- a/pkgs/development/compilers/factor-lang/factor99.nix +++ /dev/null @@ -1,224 +0,0 @@ -{ lib -, stdenv -, cairo -, curl -, fetchurl -, freealut -, gdk-pixbuf -, git -, glib -, gnome2 -, graphviz -, gtk2-x11 -, interpreter -, libGL -, libGLU -, libogg -, librsvg -, libvorbis -, makeWrapper -, ncurses -, openal -, openssl -, pango -, pcre -, runCommand -, runtimeShell -, tzdata -, udis86 -, unzip -, writeScriptBin -, zlib -}: -let - runtimeLibs = [ - cairo - freealut - gdk-pixbuf - glib - gnome2.gtkglext - graphviz - gtk2-x11 - libGL - libGLU - libogg - libvorbis - openal - openssl - pango - pcre - udis86 - zlib - ]; - - wrapFactorScript = { from, to ? false, runtimeLibs }: '' - # Set Gdk pixbuf loaders file to the one from the build dependencies here - unset GDK_PIXBUF_MODULE_FILE - # Defined in gdk-pixbuf setup hook - findGdkPixbufLoaders "${librsvg}" - - ${if (builtins.isString to) then "makeWrapper ${from} ${to}" else "wrapProgram ${from}"} \ - --set GDK_PIXBUF_MODULE_FILE "$GDK_PIXBUF_MODULE_FILE" \ - --argv0 factor \ - --prefix LD_LIBRARY_PATH : /run/opengl-driver/lib:${lib.makeLibraryPath runtimeLibs} \ - --prefix PATH : ${lib.makeBinPath [ graphviz ]} - ''; - - wrapFactor = runtimeLibs: - runCommand (lib.appendToName "with-libs" interpreter).name - { - nativeBuildInputs = [ makeWrapper ]; - buildInputs = [ gdk-pixbuf ]; - passthru.runtimeLibs = runtimeLibs ++ interpreter.runtimeLibs; - } - (wrapFactorScript { - from = "${interpreter}/lib/factor/.factor-wrapped"; - to = "$out/bin/factor"; - runtimeLibs = (runtimeLibs ++ interpreter.runtimeLibs); - }); - - # Development helper for use in nix shell - wrapLocalFactor = writeScriptBin "wrapFactor" '' - #!${runtimeShell} - ${wrapFactorScript { from = "./factor"; inherit runtimeLibs; }} - ln -sf factor.image .factor-wrapped.image - ''; - rev = "e10b64dbc53a8583098e73580a1eb9ff4ce0c709"; - version = "0.99"; - -in -stdenv.mkDerivation { - pname = "factor-lang"; - inherit version; - - src = fetchurl { - url = "https://downloads.factorcode.org/releases/${version}/factor-src-${version}.zip"; - sha256 = "f5626bb3119bd77de9ac3392fdbe188bffc26557fab3ea34f7ca21e372a8443e"; - }; - - patches = [ - ./staging-command-line-0.99-pre.patch - ./workdir-0.99-pre.patch - ./adjust-paths-in-unit-tests.patch - ]; - - nativeBuildInputs = [ git makeWrapper curl unzip wrapLocalFactor ]; - buildInputs = runtimeLibs; - - postPatch = '' - sed -ie '4i GIT_LABEL = heads/master-${rev}' GNUmakefile - - # There is no ld.so.cache in NixOS so we patch out calls to that completely. - # This should work as long as no application code relies on `find-library*` - # to return a match, which currently is the case and also a justified assumption. - - sed -ie "s#/sbin/ldconfig -p#cat $out/lib/factor/ld.so.cache#g" \ - basis/alien/libraries/finder/linux/linux.factor - - # Some other hard-coded paths to fix: - sed -i 's#/usr/share/zoneinfo/#${tzdata}/share/zoneinfo/#g' \ - extra/tzinfo/tzinfo.factor - - sed -i 's#/usr/share/terminfo#${ncurses.out}/share/terminfo#g' \ - extra/terminfo/terminfo.factor - - # De-memoize xdg-* functions, otherwise they break the image. - sed -ie 's/^MEMO:/:/' basis/xdg/xdg.factor - - # update default paths in factor-listener.el for fuel mode - substituteInPlace misc/fuel/fuel-listener.el \ - --replace '(defcustom fuel-factor-root-dir nil' "(defcustom fuel-factor-root-dir \"$out/lib/factor\"" - ''; - - buildPhase = '' - runHook preBuild - # Necessary here, because ld.so.cache is needed in its final location during rebuild. - mkdir -p $out/bin $out/lib/factor - patchShebangs ./build.sh - # Factor uses XDG_CACHE_HOME for cache during compilation. - # We can't have that. So, set it to $TMPDIR/.cache - export XDG_CACHE_HOME=$TMPDIR/.cache && mkdir -p $XDG_CACHE_HOME - - # There is no ld.so.cache in NixOS so we construct one - # out of known libraries. The side effect is that find-lib - # will work only on the known libraries. There does not seem - # to be a generic solution here. - find $(echo ${lib.makeLibraryPath runtimeLibs} | sed -e 's#:# #g') -name \*.so.\* > $TMPDIR/so.lst - (echo $(cat $TMPDIR/so.lst | wc -l) "libs found in cache \`/etc/ld.so.cache'"; - for l in $(<$TMPDIR/so.lst); do - echo " $(basename $l) (libc6,x86-64) => $l"; - done)> $out/lib/factor/ld.so.cache - - make -j$NIX_BUILD_CORES linux-x86-64 - printf "First build from upstream boot image\n" >&2 - ./build.sh bootstrap - printf "Rebuild boot image\n" >&2 - ./factor -script -e='"unix-x86.64" USING: system bootstrap.image memory ; make-image save 0 exit' - printf "Second build from local boot image\n" >&2 - ./build.sh bootstrap - runHook postBuild - ''; - - # For now, the check phase runs, but should always return 0. This way the logs - # contain the test failures until all unit tests are fixed. Then, it should - # return 1 if any test failures have occured. - doCheck = false; - checkPhase = '' - runHook preCheck - set +e - ./factor -e='USING: tools.test zealot.factor sequences namespaces formatting ; - zealot-core-vocabs "compiler" suffix [ test ] each :test-failures - test-failures get length "Number of failed Tests: %d\n" printf' - [ $? -eq 0 ] || { - mkdir -p "$out/nix-support" - touch "$out/nix-support/failed" - } - set -e - runHook postCheck - ''; - - installPhase = '' - runHook preInstall - cp -r factor factor.image LICENSE.txt README.md basis core extra misc $out/lib/factor - - # Create a wrapper in bin/ and lib/factor/ - ${wrapFactorScript { from = "$out/lib/factor/factor"; inherit runtimeLibs; }} - mv $out/lib/factor/factor.image $out/lib/factor/.factor-wrapped.image - cp $out/lib/factor/factor $out/bin/ - - # Emacs fuel expects the image being named `factor.image` in the factor base dir - ln -s $out/lib/factor/.factor-wrapped.image $out/lib/factor/factor.image - - # install fuel mode for emacs - mkdir -p $out/share/emacs/site-lisp - ln -s $out/lib/factor/misc/fuel/*.el $out/share/emacs/site-lisp/ - runHook postInstall - ''; - - passthru = { - inherit runtimeLibs wrapFactorScript; - withLibs = wrapFactor; - }; - - meta = with lib; { - homepage = "https://factorcode.org/"; - description = "Concatenative, stack-based programming language"; - longDescription = '' - The Factor programming language is a concatenative, stack-based - programming language with high-level features including dynamic types, - extensible syntax, macros, and garbage collection. On a practical side, - Factor has a full-featured library, supports many different platforms, and - has been extensively documented. - - The implementation is fully compiled for performance, while still - supporting interactive development. Factor applications are portable - between all common platforms. Factor can deploy stand-alone applications - on all platforms. Full source code for the Factor project is available - under a BSD license. - ''; - license = licenses.bsd2; - maintainers = with maintainers; [ spacefrogg ]; - platforms = lib.intersectLists platforms.x86_64 platforms.linux; - mainProgram = "factor"; - }; -} diff --git a/pkgs/development/compilers/factor-lang/ld.so.cache-from-env.patch b/pkgs/development/compilers/factor-lang/ld.so.cache-from-env.patch new file mode 100644 index 0000000000000..75c49a8cac131 --- /dev/null +++ b/pkgs/development/compilers/factor-lang/ld.so.cache-from-env.patch @@ -0,0 +1,27 @@ +diff -ur factor.orig/basis/alien/libraries/finder/linux/linux.factor factor/basis/alien/libraries/finder/linux/linux.factor +--- factor.orig/basis/alien/libraries/finder/linux/linux.factor 2024-02-09 14:38:33.966439078 +0100 ++++ factor/basis/alien/libraries/finder/linux/linux.factor 2024-02-09 14:41:16.775938179 +0100 +@@ -2,7 +2,7 @@ + ! See https://factorcode.org/license.txt for BSD license + USING: accessors alien.libraries.finder arrays assocs + combinators.short-circuit environment io io.encodings.utf8 +-io.launcher kernel make sequences sets splitting system ++io.files io.launcher kernel make sequences sets splitting system + unicode ; + IN: alien.libraries.finder.linux + +@@ -25,8 +25,12 @@ + ] map ; + + : load-ldconfig-cache ( -- seq ) +- "/sbin/ldconfig -p" utf8 [ read-lines ] with-process-reader* +- 2drop [ f ] [ rest parse-ldconfig-lines ] if-empty ; ++ "FACTOR_LD_SO_CACHE" os-env [ ++ utf8 [ read-lines ] with-file-reader ++ ] [ ++ { } clone ++ ] if* ++ [ f ] [ rest parse-ldconfig-lines ] if-empty ; + + : ldconfig-arch ( -- str ) + mach-map cpu of { "libc6" } or ; diff --git a/pkgs/development/compilers/factor-lang/mk-factor-application.nix b/pkgs/development/compilers/factor-lang/mk-factor-application.nix new file mode 100644 index 0000000000000..1ceff4a3bd7d9 --- /dev/null +++ b/pkgs/development/compilers/factor-lang/mk-factor-application.nix @@ -0,0 +1,130 @@ +{ + stdenv, + lib, + writeText, + makeWrapper, + factor-lang, + factor-no-gui, + librsvg, + gdk-pixbuf, +}@initAttrs: + +drvArgs: + +let + flang = factor-lang; # workaround to satisfy nixf-tidy +in +(stdenv.mkDerivation drvArgs).overrideAttrs ( + finalAttrs: + { + name ? "${finalAttrs.pname}-${finalAttrs.version}", + factor-lang ? if enableUI then flang else factor-no-gui, + enableUI ? false, + # Allow overriding the deployed vocabulary name. A $vocabName.factor file must exist! + vocabName ? finalAttrs.pname or name, + # Allow overriding the binary name + binName ? lib.last (lib.splitString "/" vocabName), + # Extra libraries needed, defaults to standard libraries, set [] to disable + extraLibs ? with factor-lang; defaultLibs ++ runtimeLibs, + # Extra binaries in PATH, defaults to standard paths, set [] to disable + extraPaths ? with factor-lang; defaultBins ++ binPackages, + deployScriptText ? '' + USING: command-line io io.backend io.pathnames kernel namespaces sequences + tools.deploy tools.deploy.config tools.deploy.backend vocabs.loader ; + + IN: deploy-me + + : load-and-deploy ( path/vocab -- ) + normalize-path [ + parent-directory add-vocab-root + ] [ + file-name dup reload deploy + ] bi ; + + : deploy-vocab ( path/vocab path/target -- ) + normalize-path deploy-directory set + f open-directory-after-deploy? set + load-and-deploy ; + + : deploy-me ( -- ) + command-line get dup length 2 = [ + first2 deploy-vocab + ] [ + drop + "usage: deploy-me " print + nl + ] if ; + + MAIN: deploy-me + '', + ... + }@attrs: + let + deployScript = writeText "deploy-me.factor" finalAttrs.deployScriptText; + in + { + inherit + enableUI + vocabName + deployScriptText + extraLibs + extraPaths + binName + factor-lang + ; + nativeBuildInputs = [ makeWrapper ] ++ attrs.nativeBuildInputs or [ ]; + + buildInputs = [ + finalAttrs.factor-lang + ] ++ (lib.optional enableUI gdk-pixbuf) ++ attrs.buildInputs or [ ]; + + buildPhase = + attrs.buildPhase or '' + runHook preBuild + vocabBaseName=$(basename "${finalAttrs.vocabName}") + mkdir -p "$out/lib/factor" "$TMPDIR/.cache" + export XDG_CACHE_HOME="$TMPDIR/.cache" + + ${finalAttrs.factor-lang}/bin/factor ${deployScript} "./${finalAttrs.vocabName}" "$out/lib/factor" + cp "$TMPDIR/factor-temp"/*.image "$out/lib/factor/$vocabBaseName" + runHook postBuild + ''; + + installPhase = + attrs.installPhase or ( + '' + runHook preInstall + '' + + (lib.optionalString finalAttrs.enableUI '' + # Set Gdk pixbuf loaders file to the one from the build dependencies here + unset GDK_PIXBUF_MODULE_FILE + # Defined in gdk-pixbuf setup hook + findGdkPixbufLoaders "${librsvg}" + makeWrapperArgs+=( + --set GDK_PIXBUF_MODULE_FILE "$GDK_PIXBUF_MODULE_FILE" + --prefix LD_LIBRARY_PATH : /run/opengl-driver/lib) + '') + + (lib.optionalString (finalAttrs.extraLibs != [ ]) '' + makeWrapperArgs+=( + --prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath finalAttrs.extraLibs}) + '') + + '' + mkdir -p "$out/bin" + makeWrapper "$out/lib/factor/$vocabBaseName/$vocabBaseName" \ + "$out/bin/${finalAttrs.binName}" \ + --prefix PATH : ${lib.makeBinPath finalAttrs.extraPaths} \ + "''${makeWrapperArgs[@]}" + runHook postInstall + '' + ); + + passthru = { + vocab = finalAttrs.src; + } // attrs.passthru or { }; + + meta = { + platforms = finalAttrs.factor-lang.meta.platforms; + mainProgram = finalAttrs.binName; + } // attrs.meta or { }; + } +) diff --git a/pkgs/development/compilers/factor-lang/scope.nix b/pkgs/development/compilers/factor-lang/scope.nix index af0d7af1293f1..5b9dea5c6c403 100644 --- a/pkgs/development/compilers/factor-lang/scope.nix +++ b/pkgs/development/compilers/factor-lang/scope.nix @@ -1,16 +1,22 @@ -{ lib, pkgs -, overrides ? (self: super: {})}: +{ lib, pkgs, overrides ? (self: super: { }) }: let - inside = (self: - let callPackage = pkgs.newScope self; - in rec { - interpreter = callPackage ./factor99.nix { inherit (pkgs) stdenv; }; + inside = self: + let callPackage = pkgs.newScope self; + in { - # Convenience access for using the returned attribute the same way as the - # interpreter derivation. Takes a list of runtime libraries as its only - # argument. - inherit (self.interpreter) withLibs; - }); + buildFactorApplication = callPackage ./mk-factor-application.nix { }; + + factor-unwrapped = callPackage ./unwrapped.nix { }; + + factor-lang = callPackage ./wrapper.nix { }; + factor-no-gui = callPackage ./wrapper.nix { guiSupport = false; }; + factor-minimal = callPackage ./wrapper.nix { + enableDefaults = false; + guiSupport = false; + }; + } // lib.optionalAttrs pkgs.config.allowAliases { + interpreter = builtins.throw "factor-lang-scope now offers various wrapped factor runtimes (see documentation) and the buildFactorApplication helper."; + }; extensible-self = lib.makeExtensible (lib.extends overrides inside); in extensible-self diff --git a/pkgs/development/compilers/factor-lang/unwrapped.nix b/pkgs/development/compilers/factor-lang/unwrapped.nix new file mode 100644 index 0000000000000..8d7b6dae6661c --- /dev/null +++ b/pkgs/development/compilers/factor-lang/unwrapped.nix @@ -0,0 +1,110 @@ +{ + lib, + stdenv, + fetchurl, + makeWrapper, + curl, + git, + ncurses, + tzdata, + unzip, +}: + +stdenv.mkDerivation (finalAttrs: { + pname = "factor-lang"; + version = "0.99"; + + src = fetchurl { + url = "https://downloads.factorcode.org/releases/${finalAttrs.version}/factor-src-${finalAttrs.version}.zip"; + sha256 = "f5626bb3119bd77de9ac3392fdbe188bffc26557fab3ea34f7ca21e372a8443e"; + }; + + patches = [ + # Use full path to image while bootstrapping + ./staging-command-line-0.99-pre.patch + # Point work vocabulary root to a writable location + ./workdir-0.99-pre.patch + # Patch hard-coded FHS paths + ./adjust-paths-in-unit-tests.patch + # Avoid using /sbin/ldconfig + ./ld.so.cache-from-env.patch + ]; + + nativeBuildInputs = [ + git + makeWrapper + curl + unzip + ]; + + postPatch = '' + sed -ie '4i GIT_LABEL = heads/master-'$(< git-id) GNUmakefile + # Some other hard-coded paths to fix: + substituteInPlace extra/tzinfo/tzinfo.factor \ + --replace-fail '/usr/share/zoneinfo' '${tzdata}/share/zoneinfo' + + substituteInPlace extra/terminfo/terminfo.factor \ + --replace-fail '/usr/share/terminfo' '${ncurses.out}/share/terminfo' + + # update default paths in fuel-listener.el for fuel mode + substituteInPlace misc/fuel/fuel-listener.el \ + --replace-fail '(defcustom fuel-factor-root-dir nil' "(defcustom fuel-factor-root-dir \"$out/lib/factor\"" + ''; + + dontConfigure = true; + + preBuild = '' + patchShebangs ./build.sh + # Factor uses XDG_CACHE_HOME for cache during compilation. + # We can't have that. So, set it to $TMPDIR/.cache + export XDG_CACHE_HOME=$TMPDIR/.cache + mkdir -p $XDG_CACHE_HOME + ''; + + makeTarget = "linux-x86-64"; + + postBuild = '' + printf "First build from upstream boot image\n" >&2 + ./build.sh bootstrap + printf "Rebuild boot image\n" >&2 + ./factor -script -e='"unix-x86.64" USING: system bootstrap.image memory ; make-image save 0 exit' + printf "Second build from local boot image\n" >&2 + ./build.sh bootstrap + ''; + + installPhase = '' + runHook preInstall + mkdir -p $out/lib/factor $out/share/emacs/site-lisp + cp -r factor factor.image libfactor.a libfactor-ffi-test.so \ + boot.*.image LICENSE.txt README.md basis core extra misc \ + $out/lib/factor + + # install fuel mode for emacs + ln -r -s $out/lib/factor/misc/fuel/*.el $out/share/emacs/site-lisp + runHook postInstall + ''; + + meta = with lib; { + homepage = "https://factorcode.org/"; + description = "A concatenative, stack-based programming language"; + longDescription = '' + The Factor programming language is a concatenative, stack-based + programming language with high-level features including dynamic types, + extensible syntax, macros, and garbage collection. On a practical side, + Factor has a full-featured library, supports many different platforms, and + has been extensively documented. + + The implementation is fully compiled for performance, while still + supporting interactive development. Factor applications are portable + between all common platforms. Factor can deploy stand-alone applications + on all platforms. Full source code for the Factor project is available + under a BSD license. + ''; + license = licenses.bsd2; + maintainers = with maintainers; [ + vrthra + spacefrogg + ]; + platforms = [ "x86_64-linux" ]; + }; +}) diff --git a/pkgs/development/compilers/factor-lang/wrapper.nix b/pkgs/development/compilers/factor-lang/wrapper.nix new file mode 100644 index 0000000000000..6d198d587311f --- /dev/null +++ b/pkgs/development/compilers/factor-lang/wrapper.nix @@ -0,0 +1,165 @@ +{ + lib, + stdenv, + makeWrapper, + factor-unwrapped, + cairo, + freealut, + gdk-pixbuf, + glib, + gnome2, + gtk2-x11, + libGL, + libGLU, + librsvg, + graphviz, + libogg, + libvorbis, + openal, + openssl, + pango, + pcre, + udis86, + zlib, + # Enable factor GUI support + guiSupport ? true, + # Libraries added to ld.so.cache + runtimeLibs ? [ ], + # Packages added to path (and ld.so.cache) + binPackages ? [ ], + # Extra vocabularies added to out/lib/factor + extraVocabs ? [ ], + # Enable default libs and bins to run most of the standard library code. + enableDefaults ? true, +}: +let + inherit (lib) optional optionals optionalString; + # missing from lib/strings + escapeNixString = s: lib.escape [ "$" ] (builtins.toJSON s); + toFactorArgs = x: lib.concatStringsSep " " (map escapeNixString x); + defaultLibs = optionals enableDefaults ( + [ + libogg + libvorbis + openal + openssl + pcre + udis86 + zlib + ] + ++ optionals guiSupport [ + cairo + freealut + gdk-pixbuf + glib + gnome2.gtkglext + gtk2-x11 + libGL + libGLU + pango + ] + ); + defaultBins = optionals enableDefaults [ graphviz ]; + libs = defaultLibs ++ runtimeLibs ++ binPackages; + bins = binPackages ++ defaultBins; + +in +stdenv.mkDerivation (finalAttrs: { + pname = "${factor-unwrapped.pname}-env"; + inherit (factor-unwrapped) version; + + nativeBuildInputs = [ makeWrapper ]; + buildInputs = optional guiSupport gdk-pixbuf; + + dontUnpack = true; + + installPhase = + '' + runHook preInstall + '' + + optionalString guiSupport '' + # Set Gdk pixbuf loaders file to the one from the build dependencies here + unset GDK_PIXBUF_MODULE_FILE + # Defined in gdk-pixbuf setup hook + findGdkPixbufLoaders "${librsvg}" + makeWrapperArgs+=(--set GDK_PIXBUF_MODULE_FILE "$GDK_PIXBUF_MODULE_FILE") + '' + + '' + makeWrapperArgs+=( + --prefix LD_LIBRARY_PATH : /run/opengl-driver/lib:${lib.makeLibraryPath libs} + --prefix PATH : ${lib.makeBinPath bins}) + mkdir -p "$out/bin" "$out/share" + cp -r "${factor-unwrapped}/lib" "$out/" + cp -r "${factor-unwrapped}/share/emacs" "$out/share/" + chmod -R u+w "$out/lib" "$out/share" + + for f in ${lib.escapeShellArgs extraVocabs}; do + cp -r "$f"/* $out/lib/factor + done + + # There is no ld.so.cache in NixOS so we construct one + # out of known libraries. The side effect is that find-lib + # will work only on the known libraries. There does not seem + # to be a generic solution here. + find $(echo ${ + lib.makeLibraryPath ([ stdenv.cc.libc ] ++ libs) + } | sed -e 's#:# #g') -name \*.so.\* > $TMPDIR/so.lst + (echo $(cat $TMPDIR/so.lst | wc -l) "libs found in cache \`/etc/ld.so.cache'"; + for l in $(<$TMPDIR/so.lst); do + echo " $(basename $l) (libc6,x86-64) => $l"; + done)> $out/lib/factor/ld.so.cache + + # Create a wrapper in bin/ and lib/factor/ + wrapProgram "$out/lib/factor/factor" \ + --argv0 factor \ + --set FACTOR_LD_SO_CACHE "$out/lib/factor/ld.so.cache" \ + "''${makeWrapperArgs[@]}" + mv $out/lib/factor/factor.image $out/lib/factor/.factor-wrapped.image + cp $out/lib/factor/factor $out/bin/ + + # Emacs fuel expects the image being named `factor.image` in the factor base dir + ln -rs $out/lib/factor/.factor-wrapped.image $out/lib/factor/factor.image + + # Update default paths in fuel-listener.el to new output + sed -E -i -e 's#(\(defcustom fuel-factor-root-dir ").*(")#'"\1$out/lib/factor\2#" \ + "$out/share/emacs/site-lisp/fuel-listener.el" + runHook postInstall + ''; + + doInstallCheck = true; + disabledTests = toFactorArgs [ + "io.files.info.unix" + "io.launcher.unix" + "io.ports" + "io.sockets" + "io.sockets.unix" + "io.sockets.secure.openssl" + "io.sockets.secure.unix" + ]; + installCheckPhase = '' + runHook preCheck + export HOME=$TMPDIR + $out/bin/factor -e='USING: tools.test tools.test.private + zealot.factor sequences namespaces formatting ; + zealot-core-vocabs + { ${finalAttrs.disabledTests} } without + "compiler" suffix + [ test-vocab ] each :test-failures + test-failures get length "Number of failed tests: %d\n" printf' + runHook postCheck + ''; + + passthru = { + inherit + defaultLibs + defaultBins + runtimeLibs + binPackages + extraVocabs + ; + }; + + meta = factor-unwrapped.meta // { + mainProgram = "factor"; + }; +}) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index d95cfaa0b1cef..533e7c927819f 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -20053,7 +20053,7 @@ with pkgs; faad2 = callPackage ../development/libraries/faad2 { }; factor-lang-scope = callPackage ../development/compilers/factor-lang/scope.nix { }; - factor-lang = factor-lang-scope.interpreter; + factor-lang = factor-lang-scope.factor-lang; far2l = callPackage ../applications/misc/far2l { inherit (darwin.apple_sdk.frameworks) IOKit Carbon Cocoa AudioToolbox OpenGL System;