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

typst: add initial support for typst packages #369283

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

cherrypiejam
Copy link

@cherrypiejam cherrypiejam commented Dec 30, 2024

This PR adds initial support to build Typst documents with Typst packages. In particular, it adds a Typst package builder and exposes it at the top level to ease the process of defining new Typst packages. It further adds a new function to the Typst derivation for creating a new Typst environment in which the Typst compiler understands a specific set of packages, namely typst.withPackages. Moreover, three Typst packages are defined and collected as a single attribution set as an initial demonstration.

Only a few Typst packages are included in this PR for a reason. The Typst Universe (the official site for Typst packages) maintains a single git repo that contains all Typst packages. Each package entry does not necessarily map to the same version specified in their respective git repo published by their authors (if there is). This is because the package authors need to manually move their package and copy the source, instead of a pointer, to the Typst Universe repo. Breaking the Typst Universe repo down to per package requires separate places to store each of them, and due to the aforementioned reason, the exact source may disagree with the original git repo 1. This is why, in my opinion, we might want to collect our own set of Typst packages instead of directly pulling from the Typst Universe repo. For gradual adaption, typst.withPackages also provides a method to override the predefined set of Typst packages, namely typst.withPackages.override, making it easier for end users overriding the existing set of Typst packages and integrating new packages into their project.

All packages from the Typst Universe are included in this PR, along with a maintenance script. The script simply generates a set of nix expressions for all packages from the Typst Universe. Each individual package is now fetched as a tarball from the Typst Universe instead of their individual repository.

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 25.05 Release Notes (or backporting 24.11 and 25.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 👍 reaction to pull requests you find important.

Footnotes

  1. Assuming the author(s) also publish their package in another repo outside of the Typst Universe repo.

@nix-owners nix-owners bot requested a review from philiptaron December 30, 2024 02:28
@NixOSInfra NixOSInfra added the 12. first-time contribution This PR is the author's first one; please be gentle! label Dec 30, 2024
@github-actions github-actions bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux labels Dec 30, 2024
@cherrypiejam cherrypiejam force-pushed the typst branch 3 times, most recently from b5ab425 to 0bcf7c9 Compare December 30, 2024 12:20
Copy link
Contributor

@SigmaSquadron SigmaSquadron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Welcome to Nixpkgs! This is truly amazing; I've wanted to build Typst packages for a very long time but never got around to it.

Note that the commits should be split as much as possible. Each new typstPackage should be added in a separate commit. The changes to the base typst package to wrap it should also be a separate commit.

pkgs/by-name/ty/typst/with-packages.nix Outdated Show resolved Hide resolved
pkgs/build-support/build-typst-package.nix Outdated Show resolved Hide resolved
pkgs/development/typst-packages/cetz.nix Outdated Show resolved Hide resolved
Copy link
Contributor

@SigmaSquadron SigmaSquadron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the actual package set, even though we can't import from Universe directly, it might still be a good idea to scrape it for information and build packages automatically. typst.toml gives us all of the necessary information to build each package.

pkgs/build-support/build-typst-package.nix Outdated Show resolved Hide resolved
pkgs/build-support/build-typst-package.nix Outdated Show resolved Hide resolved
pkgs/build-support/build-typst-package.nix Outdated Show resolved Hide resolved
pkgs/build-support/build-typst-package.nix Outdated Show resolved Hide resolved
pkgs/development/typst-packages/polylux.nix Outdated Show resolved Hide resolved
pkgs/development/typst-packages/polylux.nix Outdated Show resolved Hide resolved
pkgs/development/typst-packages/oxifmt.nix Outdated Show resolved Hide resolved
pkgs/development/typst-packages/cetz.nix Outdated Show resolved Hide resolved
pkgs/development/typst-packages/cetz.nix Outdated Show resolved Hide resolved
pkgs/by-name/ty/typst/with-packages.nix Outdated Show resolved Hide resolved
Copy link
Contributor

@drupol drupol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,

Thanks for tackling this, I have a few comments, let me know if you have questions.

@jtojnar : I know you're busy these days, but if you could have a look at this, it would be great.

Thanks!

pkgs/build-support/build-typst-package.nix Outdated Show resolved Hide resolved
pkgs/build-support/build-typst-package.nix Outdated Show resolved Hide resolved
pkgs/by-name/ty/typst/package.nix Outdated Show resolved Hide resolved
pkgs/by-name/ty/typst/with-packages.nix Outdated Show resolved Hide resolved
@wegank wegank added the 2.status: merge conflict This PR has merge conflicts with the target branch label Jan 4, 2025
@ofborg ofborg bot removed the 2.status: merge conflict This PR has merge conflicts with the target branch label Jan 5, 2025
@cherrypiejam
Copy link
Author

Regarding the actual package set, even though we can't import from Universe directly, it might still be a good idea to scrape it for information and build packages automatically. typst.toml gives us all of the necessary information to build each package.

Ah that is great. Didn't know they already have that information included. I'll work on the script when I got time, but it will have to be waited for another week or so.

Copy link
Contributor

@SigmaSquadron SigmaSquadron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, just one final open question for anyone who knows about packagesFromDirectoryRecursive.

pkgs/top-level/all-packages.nix Outdated Show resolved Hide resolved
@cherrypiejam cherrypiejam force-pushed the typst branch 2 times, most recently from e753a45 to 7560346 Compare January 5, 2025 19:52
pkgs/development/typst-packages/cetz.nix Outdated Show resolved Hide resolved
pkgs/top-level/typst-packages.nix Outdated Show resolved Hide resolved
@drupol
Copy link
Contributor

drupol commented Jan 6, 2025

Would it be a good idea to have some documentation for this ?

@cherrypiejam
Copy link
Author

Would it be a good idea to have some documentation for this ?

I can write it, but where should I put it?

(Module updates) Added a release notes entry if the change is significant

Not sure if this change should be considered as significant

@cherrypiejam cherrypiejam requested a review from jtojnar January 24, 2025 08:14
@cherrypiejam
Copy link
Author

All packages from the Typst Universe is here, and I added a maintenance script to generate the attribution set for them. I guess right it's just the style and comments --- e.g., do I need to add some comments at the beginning of the generated list? I am up for suggestions.

@philiptaron philiptaron dismissed their stale review January 25, 2025 00:01

Addressed I think.

@ShamrockLee
Copy link
Contributor

We can preserve the flexibility to override programmatically fetched packages and add manually defined ones without modifying the auto-generated files. We could place the generated file and the manual package definitions inside pkgs/development/typst-packages, reserve pkgs/top-level/typst-packages.nix to register the manual packages, and add some logic to prepend the auto-generated ones.

For the auto-generated files, serialization formats like JSON or TOML would be more structural and contain less boilerplate code. (TOML would be more git diff friendly than JSON as it doesn't have the trailing-comma-forbidding issue).

@ShamrockLee
Copy link
Contributor

ShamrockLee commented Jan 25, 2025

We should consider applying lib.recurseIntoAttrs to pkgs.typstPackages to make it nix search-able and allow the CI and nixpkgs-review to check them. (The current CI shows no package addition, which is incorrect.)

There should also be a way to override/extend them (e.g., typstPackages.extend), as packages inside depend on each other.

Tother with #369283 (comment) , we could have a pkgs/by-name/ty/typst/typst-packages-combined.nix that looks like:

{
  lib,
  config,
  pkgs,
  typst ? pkgs.typst,
}:

# TODO: Add splicing support
lib.makeExtensible (
  final:
  let
    typstPackagesFromUniverse = lib.mapAttrs (
      name: packageSpec:
      final.buildTypstPackage {
        # Package definitions based on packageSpec
      }
    ) (lib.importTOML ./typs-packages-from-universe.toml);
  in
  lib.recurseIntoAttrs {

    inherit typst;

    buildTypstPackage = pkgs.callPackage ../../../build-support/build-typst-package.nix { };

    callPackage = lib.callPackageWith (pkgs // final);
    callPackages = lib.callPackagesWith (pkgs // final);

    inherit typstPackagesFromUniverse;
  }
  // typstPackagesFromUniverse
  // import ../../../top-level/typst-packages.nix { inherit lib config pkgs; } final
)

and pkgs/top-level/typst-packages.nix like:

{
  lib,
  config,
  pkgs,
}:
final: {
  # Manually defined/overridden Typst packages
}

@cherrypiejam
Copy link
Author

This makes sense, but I am wondering the need for manually defined/overridden Typst packages in nixpkgs. It is possible for users to override some of packages from the Typst Universe due to outdated versions, or because they want to patch the package for some specific features. Thus it makes sense to make it extensible.

However, I don't see the reason to have a part for manually defined Typst packages in nixpkgs --- as we are not going to add new packages because we are assigning the Typst packages into the preview namespace in our typst environment and preview simply is for packages from the Typst Universe. Also currently there is no need to patch a single Typst package, as they are directly pulled from the official mirror site and they should work as they are. Because of aforementioned points, I say we leave that part behind and add it back when needed.

@cherrypiejam
Copy link
Author

cherrypiejam commented Jan 31, 2025

We should consider applying lib.recurseIntoAttrs to pkgs.typstPackages to make it nix search-able and allow the CI and nixpkgs-review to check them. (The current CI shows no package addition, which is incorrect.)

There should also be a way to override/extend them (e.g., typstPackages.extend), as packages inside depend on each other.

Tother with #369283 (comment) , we could have a pkgs/by-name/ty/typst/typst-packages-combined.nix that looks like[...]

Updated the PR accordingly. Did not include a customized typst package set simply because there is no need of doing so for now (even if we do, it's going to be an empty set). However, the package-combined is there so it would be easy to extend the package set when necessary.

@ShamrockLee
Copy link
Contributor

Considering there's no need for separate registration of overriding, we could further simplify the typstPackages expression into

{
  lib,
  callPackage,
}:

lib.makeExtensible (
  final:
  lib.recurseIntoAttrs (
    lib.mapAttrs (
      name: packageSpec:
      callPackage (
        {
          lib,
          buildTypstPackage,
          fetchurl,
        }:
        buildTypstPackage {
          inherit (packageSpec) pname version;

          src = fetchurl {
            inherit (packageSpec) url hash;
          };

          sourceRoot = ".";

          typstDeps = builtins.filter (x: x != null) (
            lib.map (d: (lib.attrsets.attrByPath [ d ] null final)) packageSpec.typstDeps
          );

          meta = {
            inherit (packageSpec) description;
            maintainers = with lib.maintainers; [ cherrypiejam ];
            license = lib.map (lib.getAttr lib.licensesSpdx) packageSpec.license;
          } // (if packageSpec ? "homepage" then { inherit (packageSpec) homepage; } else { });
        }
      ) { }
    ) (lib.importTOML ./typst-packages-from-universe.toml)
  )
)

From your previous auto-generated Nix expression, I saw that you expect the Typst packages to take typstPackages directly, so there's no need to customize callPackage and callPackages like pythonPackages does. I added an inner callPackage to give each package its own <pkg>.override.

In my previous suggestion, the typstPackagesFromUniverse attribute allows registering an overridden package that could refer to the auto-generated, previous self as typstPackagesFromUniverse.<name>. I removed it here since we no longer need it.

Some minor changes include:

  • builtins.map -> lib.map, as builtins.map is essentially an implementation of lib.map (faster than the ones implemented with the Nix Language).
  • lib.getLicenseFromSpdxId -> lib.licensesSpdx: lib.getLicensesFromSpdxId falls back to { shortName = "<input SPDX ID>"; } with a warning message, which is uncatchable without turning all warnings into errors. lib.getLicenseFromSpdxOr lets the user decide on the default behavior. After that, we have lib.spdxLicenses, which directly exposes the restructured licenses set with SPDX ID as its keys. Previous discussions can be found in lib.meta.licensesSpdx: mapping from SPDX ID to compatible licenses #333953. I am sorry for the immature design decisions I made.
    The above expression throws an attribute-missing error when the SPDX ID isn't found. If that's too strict, you may want to change it.

The file can now be called typst-packages.nix, as it doesn't "combine" the overridden packages from the top-level registration.

pkgs/by-name/ty/typst/package.nix Outdated Show resolved Hide resolved
pkgs/top-level/all-packages.nix Show resolved Hide resolved
@cherrypiejam
Copy link
Author

Thanks for the suggestions! Have patched them accordingly

@ShamrockLee
Copy link
Contributor

Now, we have an example of a license not found by its SPDX ID: packages licensed under EUPL-1.2+ couldn't find a suitable license representation under lib.licenses. Even though I think the EUPL-1.2+ case should be fixed from the lib.licenses side, typstPackages should also offer a way to handle such an exception.

@github-actions github-actions bot added 10.rebuild-darwin: 1001-2500 10.rebuild-darwin: 501+ 10.rebuild-linux: 1001-2500 10.rebuild-linux: 501+ and removed 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux labels Feb 2, 2025
@cherrypiejam
Copy link
Author

Now, we have an example of a license not found by its SPDX ID: packages licensed under EUPL-1.2+ couldn't find a suitable license representation under lib.licenses. Even though I think the EUPL-1.2+ case should be fixed from the lib.licenses side, typstPackages should also offer a way to handle such an exception.

Right the actual case is a bit more complicated. Apparently SPDX could be expressions, which means that it has its own little grammar to construct a logic license through a set of SPDX IDs (specifically, named licenses). Typst packages allow expression strings while it is not possible to perfectly translate the logic expression to Nix, as licenses in Nixpkgs are a list of named licenses (not clear how to diff AND/OR/WITH relations). "+" is also a valid token to represent a single named license id, while the helper functions in nixpkgs do not support parse any of them.

The solution for now is simply adhoc --- unparsable SPDX IDs will be replaced to one or more parsable IDs (based on its semantics). This is hardcoded in the maintaining script. The logical relations are simply ignored due to the aforementioned reason in nixpkgs.

@cherrypiejam
Copy link
Author

cherrypiejam commented Feb 2, 2025

I see the WITH relation is encoded as an independent license in nixpkgs, but the same argument still apply for AND/OR and "+"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants