Reimplement notice file generation for third-party licenses through Rust, now with CEF credits (#3808)
This commit is contained in:
parent
4090f6c980
commit
da7437c023
|
|
@ -70,6 +70,7 @@ jobs:
|
||||||
cargo binstall --no-confirm --force "wasm-bindgen-cli@$env:WASM_BINDGEN_CLI_VERSION"
|
cargo binstall --no-confirm --force "wasm-bindgen-cli@$env:WASM_BINDGEN_CLI_VERSION"
|
||||||
|
|
||||||
- name: Build Windows Bundle
|
- name: Build Windows Bundle
|
||||||
|
shell: bash # `cargo-about` refuses to run in powershell
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
run: npm run build-desktop
|
run: npm run build-desktop
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ jobs:
|
||||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||||
|
|
||||||
- name: Build graphene raster nodes shaders
|
- name: Build graphene raster nodes shaders
|
||||||
run: nix build .nix#raster-nodes-shaders && cp result raster_nodes_shaders_entrypoint.wgsl
|
run: nix build .nix#graphite-raster-nodes-shaders && cp result raster_nodes_shaders_entrypoint.wgsl
|
||||||
|
|
||||||
- name: Upload graphene raster nodes shaders to artifacts repository
|
- name: Upload graphene raster nodes shaders to artifacts repository
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
branding/
|
branding/
|
||||||
target/
|
target/
|
||||||
|
third-party-licenses.txt*
|
||||||
result/
|
result/
|
||||||
.flatpak-builder/
|
.flatpak-builder/
|
||||||
*.spv
|
*.spv
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{ pkgs, inputs, ... }:
|
{ pkgs, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
cefPath = pkgs.cef-binary.overrideAttrs (finalAttrs: {
|
cefPath = pkgs.cef-binary.overrideAttrs (finalAttrs: {
|
||||||
|
|
@ -10,6 +10,8 @@ let
|
||||||
mv ./Resources/* $out/
|
mv ./Resources/* $out/
|
||||||
mv ./include $out/
|
mv ./include $out/
|
||||||
|
|
||||||
|
cat ./CREDITS.html | ${pkgs.xz}/bin/xz -9 -e -c > $out/CREDITS.html.xz
|
||||||
|
|
||||||
echo '${
|
echo '${
|
||||||
builtins.toJSON {
|
builtins.toJSON {
|
||||||
type = "minimal";
|
type = "minimal";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{ pkgs, inputs, ... }:
|
{ pkgs, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
extensions = [
|
extensions = [
|
||||||
|
|
|
||||||
60
.nix/dev.nix
60
.nix/dev.nix
|
|
@ -1,16 +1,58 @@
|
||||||
{
|
{ pkgs, deps, ... }:
|
||||||
pkgs,
|
|
||||||
deps,
|
|
||||||
libs,
|
|
||||||
tools,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
|
|
||||||
|
let
|
||||||
|
libs = [
|
||||||
|
pkgs.wayland
|
||||||
|
pkgs.vulkan-loader
|
||||||
|
pkgs.libGL
|
||||||
|
pkgs.openssl
|
||||||
|
pkgs.libraw
|
||||||
|
|
||||||
|
# X11 Support
|
||||||
|
pkgs.libxkbcommon
|
||||||
|
pkgs.libXcursor
|
||||||
|
pkgs.libxcb
|
||||||
|
pkgs.libX11
|
||||||
|
];
|
||||||
|
in
|
||||||
pkgs.mkShell (
|
pkgs.mkShell (
|
||||||
{
|
{
|
||||||
packages = tools.all ++ libs.all;
|
packages = libs ++ [
|
||||||
|
pkgs.pkg-config
|
||||||
|
|
||||||
LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath libs.all}:${deps.cef.env.CEF_PATH}";
|
pkgs.lld
|
||||||
|
pkgs.nodejs
|
||||||
|
pkgs.nodePackages.npm
|
||||||
|
pkgs.binaryen
|
||||||
|
pkgs.wasm-bindgen-cli_0_2_100
|
||||||
|
pkgs.wasm-pack
|
||||||
|
pkgs.cargo-about
|
||||||
|
|
||||||
|
pkgs.rustc
|
||||||
|
pkgs.cargo
|
||||||
|
pkgs.rust-analyzer
|
||||||
|
pkgs.clippy
|
||||||
|
pkgs.rustfmt
|
||||||
|
|
||||||
|
pkgs.git
|
||||||
|
|
||||||
|
pkgs.cargo-watch
|
||||||
|
pkgs.cargo-nextest
|
||||||
|
pkgs.cargo-expand
|
||||||
|
|
||||||
|
# Linker
|
||||||
|
pkgs.mold
|
||||||
|
|
||||||
|
# Profiling tools
|
||||||
|
pkgs.gnuplot
|
||||||
|
pkgs.samply
|
||||||
|
pkgs.cargo-flamegraph
|
||||||
|
|
||||||
|
# Plotting tools
|
||||||
|
pkgs.graphviz
|
||||||
|
];
|
||||||
|
|
||||||
|
LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath libs}:${deps.cef.env.CEF_PATH}";
|
||||||
XDG_DATA_DIRS = "${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}:${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}:$XDG_DATA_DIRS";
|
XDG_DATA_DIRS = "${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}:${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}:$XDG_DATA_DIRS";
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
|
|
|
||||||
|
|
@ -29,24 +29,6 @@
|
||||||
"url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
|
"url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1731533236,
|
|
||||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1770197578,
|
"lastModified": 1770197578,
|
||||||
|
|
@ -67,7 +49,6 @@
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"crane": "crane",
|
"crane": "crane",
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
}
|
}
|
||||||
|
|
@ -91,21 +72,6 @@
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|
|
||||||
185
.nix/flake.nix
185
.nix/flake.nix
|
|
@ -18,7 +18,6 @@
|
||||||
url = "github:oxalica/rust-overlay";
|
url = "github:oxalica/rust-overlay";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
|
||||||
crane.url = "github:ipetkov/crane";
|
crane.url = "github:ipetkov/crane";
|
||||||
|
|
||||||
# This is used to provide a identical development shell at `shell.nix` for users that do not use flakes
|
# This is used to provide a identical development shell at `shell.nix` for users that do not use flakes
|
||||||
|
|
@ -27,16 +26,19 @@
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
inputs:
|
inputs:
|
||||||
inputs.flake-utils.lib.eachDefaultSystem (
|
(
|
||||||
system:
|
|
||||||
let
|
let
|
||||||
info = {
|
systems = [
|
||||||
pname = "graphite";
|
"x86_64-linux"
|
||||||
version = "unstable";
|
"aarch64-linux"
|
||||||
src = pkgs.lib.cleanSourceWith {
|
];
|
||||||
src = ./..;
|
forAllSystems = f: inputs.nixpkgs.lib.genAttrs systems (system: f system);
|
||||||
filter = path: type: !(type == "directory" && builtins.baseNameOf path == ".nix");
|
args =
|
||||||
};
|
system:
|
||||||
|
(
|
||||||
|
let
|
||||||
|
lib = inputs.nixpkgs.lib // {
|
||||||
|
call = p: import p args;
|
||||||
};
|
};
|
||||||
|
|
||||||
pkgs = import inputs.nixpkgs {
|
pkgs = import inputs.nixpkgs {
|
||||||
|
|
@ -44,126 +46,65 @@
|
||||||
overlays = [ (import inputs.rust-overlay) ];
|
overlays = [ (import inputs.rust-overlay) ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
info = {
|
||||||
|
pname = "graphite";
|
||||||
|
version = "unstable";
|
||||||
|
src = inputs.nixpkgs.lib.cleanSourceWith {
|
||||||
|
src = ./..;
|
||||||
|
filter = path: type: !(type == "directory" && builtins.baseNameOf path == ".nix");
|
||||||
|
};
|
||||||
|
cargoVendored = deps.crane.lib.vendorCargoDeps { inherit (info) src; };
|
||||||
|
};
|
||||||
|
|
||||||
deps = {
|
deps = {
|
||||||
crane = import ./deps/crane.nix { inherit pkgs inputs; };
|
crane = lib.call ./deps/crane.nix;
|
||||||
cef = import ./deps/cef.nix { inherit pkgs inputs; };
|
cef = lib.call ./deps/cef.nix;
|
||||||
rustGPU = import ./deps/rust-gpu.nix { inherit pkgs inputs; };
|
rustGPU = lib.call ./deps/rust-gpu.nix;
|
||||||
};
|
};
|
||||||
|
|
||||||
libs = rec {
|
args = {
|
||||||
desktop = [
|
inherit system;
|
||||||
pkgs.wayland
|
inherit (inputs) self;
|
||||||
pkgs.openssl
|
inherit inputs;
|
||||||
pkgs.vulkan-loader
|
inherit pkgs;
|
||||||
pkgs.libraw
|
inherit lib;
|
||||||
pkgs.libGL
|
inherit info;
|
||||||
];
|
inherit deps;
|
||||||
desktop-x11 = [
|
}
|
||||||
pkgs.libxkbcommon
|
// inputs;
|
||||||
pkgs.xorg.libXcursor
|
in
|
||||||
pkgs.xorg.libxcb
|
args
|
||||||
pkgs.xorg.libX11
|
);
|
||||||
];
|
withArgs = f: forAllSystems (system: f (args system));
|
||||||
desktop-all = desktop ++ desktop-x11;
|
|
||||||
all = desktop-all;
|
|
||||||
};
|
|
||||||
|
|
||||||
tools = rec {
|
|
||||||
desktop = [
|
|
||||||
pkgs.pkg-config
|
|
||||||
];
|
|
||||||
frontend = [
|
|
||||||
pkgs.lld
|
|
||||||
pkgs.nodejs
|
|
||||||
pkgs.nodePackages.npm
|
|
||||||
pkgs.binaryen
|
|
||||||
pkgs.wasm-bindgen-cli_0_2_100
|
|
||||||
pkgs.wasm-pack
|
|
||||||
pkgs.cargo-about
|
|
||||||
];
|
|
||||||
dev = [
|
|
||||||
pkgs.rustc
|
|
||||||
pkgs.cargo
|
|
||||||
pkgs.rust-analyzer
|
|
||||||
pkgs.clippy
|
|
||||||
pkgs.rustfmt
|
|
||||||
|
|
||||||
pkgs.git
|
|
||||||
|
|
||||||
pkgs.cargo-watch
|
|
||||||
pkgs.cargo-nextest
|
|
||||||
pkgs.cargo-expand
|
|
||||||
|
|
||||||
# Linker
|
|
||||||
pkgs.mold
|
|
||||||
|
|
||||||
# Profiling tools
|
|
||||||
pkgs.gnuplot
|
|
||||||
pkgs.samply
|
|
||||||
pkgs.cargo-flamegraph
|
|
||||||
|
|
||||||
# Plotting tools
|
|
||||||
pkgs.graphviz
|
|
||||||
];
|
|
||||||
all = desktop ++ frontend ++ dev;
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
packages = rec {
|
packages = withArgs (
|
||||||
graphiteWithArgs =
|
{ lib, ... }:
|
||||||
args:
|
rec {
|
||||||
(import ./pkgs/graphite.nix {
|
|
||||||
pkgs = pkgs // {
|
|
||||||
inherit raster-nodes-shaders;
|
|
||||||
};
|
|
||||||
inherit
|
|
||||||
info
|
|
||||||
inputs
|
|
||||||
deps
|
|
||||||
libs
|
|
||||||
tools
|
|
||||||
;
|
|
||||||
})
|
|
||||||
args;
|
|
||||||
graphite = graphiteWithArgs { };
|
|
||||||
graphite-dev = graphiteWithArgs { dev = true; };
|
|
||||||
graphite-without-resources = graphiteWithArgs { embeddedResources = false; };
|
|
||||||
graphite-without-resources-dev = graphiteWithArgs {
|
|
||||||
embeddedResources = false;
|
|
||||||
dev = true;
|
|
||||||
};
|
|
||||||
graphite-bundle = import ./pkgs/graphite-bundle.nix {
|
|
||||||
inherit pkgs graphite;
|
|
||||||
};
|
|
||||||
graphite-flatpak-manifest = import ./pkgs/graphite-flatpak-manifest.nix {
|
|
||||||
inherit pkgs;
|
|
||||||
archive = graphite-bundle.tar;
|
|
||||||
};
|
|
||||||
#TODO: graphene-cli = import ./pkgs/graphene-cli.nix { inherit info pkgs inputs deps libs tools; };
|
|
||||||
raster-nodes-shaders = import ./pkgs/raster-nodes-shaders.nix {
|
|
||||||
inherit
|
|
||||||
info
|
|
||||||
pkgs
|
|
||||||
inputs
|
|
||||||
deps
|
|
||||||
libs
|
|
||||||
tools
|
|
||||||
;
|
|
||||||
};
|
|
||||||
|
|
||||||
default = graphite;
|
default = graphite;
|
||||||
};
|
graphite = (lib.call ./pkgs/graphite.nix) { };
|
||||||
|
graphite-dev = (lib.call ./pkgs/graphite.nix) { dev = true; };
|
||||||
|
graphite-raster-nodes-shaders = lib.call ./pkgs/graphite-raster-nodes-shaders.nix;
|
||||||
|
graphite-branding = lib.call ./pkgs/graphite-branding.nix;
|
||||||
|
graphite-bundle = lib.call ./pkgs/graphite-bundle.nix;
|
||||||
|
graphite-flatpak-manifest = lib.call ./pkgs/graphite-flatpak-manifest.nix;
|
||||||
|
|
||||||
devShells.default = import ./dev.nix {
|
# TODO: graphene-cli = lib.call ./pkgs/graphene-cli.nix;
|
||||||
inherit
|
|
||||||
pkgs
|
|
||||||
deps
|
|
||||||
libs
|
|
||||||
tools
|
|
||||||
;
|
|
||||||
};
|
|
||||||
|
|
||||||
formatter = pkgs.nixfmt-tree;
|
tools = {
|
||||||
|
third-party-licenses = lib.call ./pkgs/tools/third-party-licenses.nix;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
devShells = withArgs (
|
||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
default = lib.call ./dev.nix;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
formatter = withArgs ({ pkgs, ... }: pkgs.nixfmt-tree);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
{ info, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
brandingTar = pkgs.fetchurl (
|
||||||
|
let
|
||||||
|
lockContent = builtins.readFile "${info.src}/.branding";
|
||||||
|
lines = builtins.filter (s: s != [ ]) (builtins.split "\n" lockContent);
|
||||||
|
url = builtins.elemAt lines 0;
|
||||||
|
hash = builtins.elemAt lines 1;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
url = url;
|
||||||
|
sha256 = hash;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
pkgs.runCommand "${info.pname}-branding" { } ''
|
||||||
|
mkdir -p $out
|
||||||
|
tar -xvf ${brandingTar} -C $out --strip-components 1
|
||||||
|
''
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
graphite,
|
self,
|
||||||
|
system,
|
||||||
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
bundle =
|
bundle =
|
||||||
{
|
{
|
||||||
pkgs,
|
|
||||||
graphite,
|
|
||||||
archive ? false,
|
archive ? false,
|
||||||
compression ? null,
|
compression ? null,
|
||||||
passthru ? {},
|
passthru ? { },
|
||||||
}:
|
}:
|
||||||
(
|
(
|
||||||
let
|
let
|
||||||
|
graphite = self.packages.${system}.graphite;
|
||||||
tar = if compression == null then archive else true;
|
tar = if compression == null then archive else true;
|
||||||
nameArchiveSuffix = if tar then ".tar" else "";
|
nameArchiveSuffix = if tar then ".tar" else "";
|
||||||
nameCompressionSuffix = if compression == null then "" else "." + compression;
|
nameCompressionSuffix = if compression == null then "" else "." + compression;
|
||||||
|
|
@ -75,18 +76,14 @@ let
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
bundle {
|
bundle {
|
||||||
inherit pkgs graphite;
|
|
||||||
passthru = {
|
passthru = {
|
||||||
tar = bundle {
|
tar = bundle {
|
||||||
inherit pkgs graphite;
|
|
||||||
archive = true;
|
archive = true;
|
||||||
passthru = {
|
passthru = {
|
||||||
gz = bundle {
|
gz = bundle {
|
||||||
inherit pkgs graphite;
|
|
||||||
compression = "gz";
|
compression = "gz";
|
||||||
};
|
};
|
||||||
xz = bundle {
|
xz = bundle {
|
||||||
inherit pkgs graphite;
|
|
||||||
compression = "xz";
|
compression = "xz";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
archive,
|
self,
|
||||||
|
system,
|
||||||
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
(pkgs.formats.json { }).generate "art.graphite.Graphite.json" {
|
(pkgs.formats.json { }).generate "art.graphite.Graphite.json" {
|
||||||
|
|
@ -28,7 +30,7 @@
|
||||||
sources = [
|
sources = [
|
||||||
{
|
{
|
||||||
type = "archive";
|
type = "archive";
|
||||||
path = archive;
|
path = self.packages.${system}.graphite-bundle.tar;
|
||||||
strip-components = 0;
|
strip-components = 0;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,4 @@
|
||||||
{
|
{ info, deps, ... }:
|
||||||
info,
|
|
||||||
pkgs,
|
|
||||||
inputs,
|
|
||||||
deps,
|
|
||||||
libs,
|
|
||||||
tools,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
|
|
||||||
(deps.crane.lib.overrideToolchain (_: deps.rustGPU.toolchain)).buildPackage {
|
(deps.crane.lib.overrideToolchain (_: deps.rustGPU.toolchain)).buildPackage {
|
||||||
pname = "raster-nodes-shaders";
|
pname = "raster-nodes-shaders";
|
||||||
|
|
@ -16,7 +8,7 @@
|
||||||
inherit (deps.crane.lib.findCargoFiles (deps.crane.lib.cleanCargoSource info.src)) cargoConfigs;
|
inherit (deps.crane.lib.findCargoFiles (deps.crane.lib.cleanCargoSource info.src)) cargoConfigs;
|
||||||
cargoLockList = [
|
cargoLockList = [
|
||||||
"${info.src}/Cargo.lock"
|
"${info.src}/Cargo.lock"
|
||||||
"${deps.rustGPU.toolchain.passthru.availableComponents.rust-src}/lib/rustlib/src/rust/library/Cargo.lock"
|
"${deps.rustGPU.toolchain.availableComponents.rust-src}/lib/rustlib/src/rust/library/Cargo.lock"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1,42 +1,36 @@
|
||||||
{
|
{
|
||||||
info,
|
info,
|
||||||
pkgs,
|
pkgs,
|
||||||
inputs,
|
self,
|
||||||
deps,
|
deps,
|
||||||
libs,
|
system,
|
||||||
tools,
|
lib,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
{
|
{
|
||||||
embeddedResources ? true,
|
|
||||||
dev ? false,
|
dev ? false,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
brandingTar = pkgs.fetchurl (
|
branding = self.packages.${system}.graphite-branding;
|
||||||
let
|
|
||||||
lockContent = builtins.readFile "${info.src}/.branding";
|
|
||||||
lines = builtins.filter (s: s != [ ]) (builtins.split "\n" lockContent);
|
|
||||||
url = builtins.elemAt lines 0;
|
|
||||||
hash = builtins.elemAt lines 1;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
url = url;
|
|
||||||
sha256 = hash;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
branding = pkgs.runCommand "${info.pname}-branding" { } ''
|
|
||||||
mkdir -p $out
|
|
||||||
tar -xvf ${brandingTar} -C $out --strip-components 1
|
|
||||||
'';
|
|
||||||
cargoVendorDir = deps.crane.lib.vendorCargoDeps { inherit (info) src; };
|
cargoVendorDir = deps.crane.lib.vendorCargoDeps { inherit (info) src; };
|
||||||
resourcesCommon = {
|
resourcesCommon = {
|
||||||
pname = "${info.pname}-resources";
|
pname = "${info.pname}-resources";
|
||||||
inherit (info) version src;
|
inherit (info) version src;
|
||||||
inherit cargoVendorDir;
|
inherit cargoVendorDir;
|
||||||
strictDeps = true;
|
strictDeps = true;
|
||||||
nativeBuildInputs = tools.frontend;
|
nativeBuildInputs = [
|
||||||
|
pkgs.pkg-config
|
||||||
|
pkgs.lld
|
||||||
|
pkgs.nodejs
|
||||||
|
pkgs.nodePackages.npm
|
||||||
|
pkgs.binaryen
|
||||||
|
pkgs.wasm-bindgen-cli_0_2_100
|
||||||
|
pkgs.wasm-pack
|
||||||
|
pkgs.cargo-about
|
||||||
|
];
|
||||||
|
buildInputs = [ pkgs.openssl ];
|
||||||
env.CARGO_PROFILE = if dev then "dev" else "release";
|
env.CARGO_PROFILE = if dev then "dev" else "release";
|
||||||
cargoExtraArgs = "--target wasm32-unknown-unknown -p graphite-wasm --no-default-features --features native";
|
cargoExtraArgs = "--target wasm32-unknown-unknown -p graphite-wasm --no-default-features --features native";
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
|
|
@ -54,7 +48,11 @@ let
|
||||||
npmConfigScript = "setup";
|
npmConfigScript = "setup";
|
||||||
makeCacheWritable = true;
|
makeCacheWritable = true;
|
||||||
|
|
||||||
nativeBuildInputs = tools.frontend ++ [ pkgs.importNpmLock.npmConfigHook pkgs.removeReferencesTo ];
|
nativeBuildInputs = [
|
||||||
|
pkgs.importNpmLock.npmConfigHook
|
||||||
|
pkgs.removeReferencesTo
|
||||||
|
]
|
||||||
|
++ resourcesCommon.nativeBuildInputs;
|
||||||
|
|
||||||
prePatch = ''
|
prePatch = ''
|
||||||
mkdir branding
|
mkdir branding
|
||||||
|
|
@ -80,18 +78,33 @@ let
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
libs = [
|
||||||
|
pkgs.wayland
|
||||||
|
pkgs.vulkan-loader
|
||||||
|
pkgs.libGL
|
||||||
|
pkgs.openssl
|
||||||
|
pkgs.libraw
|
||||||
|
|
||||||
|
# X11 Support
|
||||||
|
pkgs.libxkbcommon
|
||||||
|
pkgs.libXcursor
|
||||||
|
pkgs.libxcb
|
||||||
|
pkgs.libX11
|
||||||
|
];
|
||||||
common = {
|
common = {
|
||||||
inherit (info) pname version src;
|
inherit (info) pname version src;
|
||||||
inherit cargoVendorDir;
|
inherit cargoVendorDir;
|
||||||
strictDeps = true;
|
strictDeps = true;
|
||||||
buildInputs = libs.desktop-all;
|
buildInputs = libs;
|
||||||
nativeBuildInputs = tools.desktop ++ [ pkgs.makeWrapper pkgs.removeReferencesTo ];
|
nativeBuildInputs = [
|
||||||
|
pkgs.pkg-config
|
||||||
|
pkgs.cargo-about
|
||||||
|
pkgs.removeReferencesTo
|
||||||
|
];
|
||||||
env = deps.cef.env // {
|
env = deps.cef.env // {
|
||||||
CARGO_PROFILE = if dev then "dev" else "release";
|
CARGO_PROFILE = if dev then "dev" else "release";
|
||||||
};
|
};
|
||||||
cargoExtraArgs = "-p graphite-desktop${
|
cargoExtraArgs = "-p graphite-desktop";
|
||||||
if embeddedResources then "" else " --no-default-features --features recommended"
|
|
||||||
}";
|
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
@ -101,31 +114,34 @@ deps.crane.lib.buildPackage (
|
||||||
// {
|
// {
|
||||||
cargoArtifacts = deps.crane.lib.buildDepsOnly common;
|
cargoArtifacts = deps.crane.lib.buildDepsOnly common;
|
||||||
|
|
||||||
env =
|
env = common.env // {
|
||||||
common.env
|
RASTER_NODES_SHADER_PATH = self.packages.${system}.graphite-raster-nodes-shaders;
|
||||||
// {
|
|
||||||
RASTER_NODES_SHADER_PATH = pkgs.raster-nodes-shaders;
|
|
||||||
}
|
|
||||||
// (
|
|
||||||
if embeddedResources then
|
|
||||||
{
|
|
||||||
EMBEDDED_RESOURCES = resources;
|
EMBEDDED_RESOURCES = resources;
|
||||||
}
|
GRAPHITE_GIT_COMMIT_HASH = self.rev or "unknown";
|
||||||
else
|
GRAPHITE_GIT_COMMIT_DATE = self.lastModified or "unknown";
|
||||||
{ }
|
|
||||||
) // {
|
|
||||||
GRAPHITE_GIT_COMMIT_HASH = inputs.self.rev or "unknown";
|
|
||||||
GRAPHITE_GIT_COMMIT_DATE = inputs.self.lastModified or "unknown";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
postUnpack = ''
|
npmDeps = pkgs.importNpmLock {
|
||||||
mkdir ./branding
|
npmRoot = "${info.src}/frontend";
|
||||||
cp -r ${branding}/* ./branding
|
};
|
||||||
'';
|
npmRoot = "frontend";
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkgs.importNpmLock.npmConfigHook
|
||||||
|
pkgs.nodePackages.npm
|
||||||
|
]
|
||||||
|
++ common.nativeBuildInputs;
|
||||||
|
|
||||||
preBuild = if inputs.self ? rev then ''
|
preBuild = ''
|
||||||
|
${lib.getExe self.packages.${system}.tools.third-party-licenses}
|
||||||
|
''
|
||||||
|
+ (
|
||||||
|
if self ? rev then
|
||||||
|
''
|
||||||
export GRAPHITE_GIT_COMMIT_DATE="$(date -u -d "@$GRAPHITE_GIT_COMMIT_DATE" +"%Y-%m-%dT%H:%M:%SZ")"
|
export GRAPHITE_GIT_COMMIT_DATE="$(date -u -d "@$GRAPHITE_GIT_COMMIT_DATE" +"%Y-%m-%dT%H:%M:%SZ")"
|
||||||
'' else "";
|
''
|
||||||
|
else
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
mkdir -p $out/bin
|
mkdir -p $out/bin
|
||||||
|
|
@ -148,7 +164,7 @@ deps.crane.lib.buildPackage (
|
||||||
remove-references-to -t "${cargoVendorDir}" $out/bin/graphite
|
remove-references-to -t "${cargoVendorDir}" $out/bin/graphite
|
||||||
|
|
||||||
patchelf \
|
patchelf \
|
||||||
--set-rpath "${pkgs.lib.makeLibraryPath libs.desktop-all}:${deps.cef.env.CEF_PATH}" \
|
--set-rpath "${pkgs.lib.makeLibraryPath libs}:${deps.cef.env.CEF_PATH}" \
|
||||||
--add-needed libGL.so \
|
--add-needed libGL.so \
|
||||||
$out/bin/graphite
|
$out/bin/graphite
|
||||||
'';
|
'';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
info,
|
||||||
|
deps,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cargoVendorDir = deps.crane.lib.vendorCargoDeps { inherit (info) src; };
|
||||||
|
common = {
|
||||||
|
pname = "third-party-licenses";
|
||||||
|
inherit (info) version src;
|
||||||
|
inherit cargoVendorDir;
|
||||||
|
nativeBuildInputs = [ pkgs.pkg-config ];
|
||||||
|
buildInputs = [ pkgs.openssl ];
|
||||||
|
strictDeps = true;
|
||||||
|
env = deps.cef.env // {
|
||||||
|
CARGO_PROFILE = "dev";
|
||||||
|
};
|
||||||
|
cargoExtraArgs = "-p third-party-licenses --features desktop";
|
||||||
|
doCheck = false;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
deps.crane.lib.buildPackage (
|
||||||
|
common
|
||||||
|
// {
|
||||||
|
inherit cargoVendorDir;
|
||||||
|
cargoArtifacts = deps.crane.lib.buildDepsOnly common;
|
||||||
|
meta.mainProgram = "third-party-licenses";
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
@ -1302,6 +1302,29 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cssparser"
|
||||||
|
version = "0.36.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2"
|
||||||
|
dependencies = [
|
||||||
|
"cssparser-macros",
|
||||||
|
"dtoa-short",
|
||||||
|
"itoa",
|
||||||
|
"phf",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cssparser-macros"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctor"
|
name = "ctor"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
|
|
@ -1475,8 +1498,7 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "download-cef"
|
name = "download-cef"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/timon-schelling/cef-rs.git?branch=graphite#8efeb241d1837447eccaee5d713a7c1ce331cd52"
|
||||||
checksum = "d98178d9254efef0f69c1f584713d69c790ec00668cd98f783a5085fbefdbddc"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bzip2",
|
"bzip2",
|
||||||
"clap",
|
"clap",
|
||||||
|
|
@ -1499,6 +1521,21 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dtoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dtoa-short"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
|
||||||
|
dependencies = [
|
||||||
|
"dtoa",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dunce"
|
name = "dunce"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
|
@ -1531,6 +1568,12 @@ dependencies = [
|
||||||
"graphite-editor",
|
"graphite-editor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ego-tree"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
|
|
@ -1911,6 +1954,16 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futf"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
|
||||||
|
dependencies = [
|
||||||
|
"mac",
|
||||||
|
"new_debug_unreachable",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
|
|
@ -2044,6 +2097,15 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getopts"
|
||||||
|
version = "0.2.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
|
@ -2365,6 +2427,7 @@ dependencies = [
|
||||||
"glam",
|
"glam",
|
||||||
"graphite-desktop-embedded-resources",
|
"graphite-desktop-embedded-resources",
|
||||||
"graphite-desktop-wrapper",
|
"graphite-desktop-wrapper",
|
||||||
|
"lzma-rust2",
|
||||||
"muda",
|
"muda",
|
||||||
"objc2 0.6.3",
|
"objc2 0.6.3",
|
||||||
"objc2-app-kit 0.3.2",
|
"objc2-app-kit 0.3.2",
|
||||||
|
|
@ -2611,6 +2674,16 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "html5ever"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"markup5ever",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
|
|
@ -3374,6 +3447,21 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lzma-rust2"
|
||||||
|
version = "0.16.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47bb1e988e6fb779cf720ad431242d3f03167c1b3f2b1aae7f1a94b2495b36ae"
|
||||||
|
dependencies = [
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mac"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "malloc_buf"
|
name = "malloc_buf"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
|
|
@ -3383,6 +3471,17 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markup5ever"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"tendril",
|
||||||
|
"web_atoms",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchers"
|
name = "matchers"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
@ -4282,6 +4381,59 @@ dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
|
||||||
|
dependencies = [
|
||||||
|
"phf_macros",
|
||||||
|
"phf_shared",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_codegen"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator",
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_generator"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_macros"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator",
|
||||||
|
"phf_shared",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_shared"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pico-args"
|
name = "pico-args"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
|
@ -4472,6 +4624,12 @@ dependencies = [
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "precomputed-hash"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "preprocessor"
|
name = "preprocessor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -5353,6 +5511,21 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scraper"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93cecd86d6259499c844440546d02f55f3e17bd286e529e48d1f9f67e92315cb"
|
||||||
|
dependencies = [
|
||||||
|
"cssparser",
|
||||||
|
"ego-tree",
|
||||||
|
"getopts",
|
||||||
|
"html5ever",
|
||||||
|
"precomputed-hash",
|
||||||
|
"selectors",
|
||||||
|
"tendril",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sctk-adwaita"
|
name = "sctk-adwaita"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
|
@ -5389,6 +5562,25 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "selectors"
|
||||||
|
version = "0.33.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "feef350c36147532e1b79ea5c1f3791373e61cbd9a6a2615413b3807bb164fb7"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.0",
|
||||||
|
"cssparser",
|
||||||
|
"derive_more",
|
||||||
|
"log",
|
||||||
|
"new_debug_unreachable",
|
||||||
|
"phf",
|
||||||
|
"phf_codegen",
|
||||||
|
"precomputed-hash",
|
||||||
|
"rustc-hash 2.1.1",
|
||||||
|
"servo_arc",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.26"
|
version = "1.0.26"
|
||||||
|
|
@ -5492,6 +5684,15 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "servo_arc"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930"
|
||||||
|
dependencies = [
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1_smol"
|
name = "sha1_smol"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
@ -5809,6 +6010,30 @@ dependencies = [
|
||||||
"float-cmp",
|
"float-cmp",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "string_cache"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901"
|
||||||
|
dependencies = [
|
||||||
|
"new_debug_unreachable",
|
||||||
|
"parking_lot",
|
||||||
|
"phf_shared",
|
||||||
|
"precomputed-hash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "string_cache_codegen"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator",
|
||||||
|
"phf_shared",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
|
@ -5981,6 +6206,17 @@ dependencies = [
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tendril"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
|
||||||
|
dependencies = [
|
||||||
|
"futf",
|
||||||
|
"mac",
|
||||||
|
"utf-8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
|
|
@ -6005,6 +6241,17 @@ dependencies = [
|
||||||
"vector-types",
|
"vector-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "third-party-licenses"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"cef-dll-sys",
|
||||||
|
"lzma-rust2",
|
||||||
|
"scraper",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
|
|
@ -6997,6 +7244,18 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web_atoms"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576"
|
||||||
|
dependencies = [
|
||||||
|
"phf",
|
||||||
|
"phf_codegen",
|
||||||
|
"string_cache",
|
||||||
|
"string_cache_codegen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-root-certs"
|
name = "webpki-root-certs"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ members = [
|
||||||
"node-graph/preprocessor",
|
"node-graph/preprocessor",
|
||||||
"proc-macros",
|
"proc-macros",
|
||||||
"tools/crate-hierarchy-viz",
|
"tools/crate-hierarchy-viz",
|
||||||
|
"tools/third-party-licenses",
|
||||||
"tools/editor-message-tree",
|
"tools/editor-message-tree",
|
||||||
"tools/node-docs",
|
"tools/node-docs",
|
||||||
]
|
]
|
||||||
|
|
@ -247,6 +248,8 @@ clap = "4.5"
|
||||||
spirv-std = { git = "https://github.com/Firestar99/rust-gpu-new", rev = "c12f216121820580731440ee79ebc7403d6ea04f", features = ["bytemuck"] }
|
spirv-std = { git = "https://github.com/Firestar99/rust-gpu-new", rev = "c12f216121820580731440ee79ebc7403d6ea04f", features = ["bytemuck"] }
|
||||||
cargo-gpu = { git = "https://github.com/Firestar99/cargo-gpu", rev = "3952a22d16edbd38689f3a876e417899f21e1fe7", default-features = false }
|
cargo-gpu = { git = "https://github.com/Firestar99/cargo-gpu", rev = "3952a22d16edbd38689f3a876e417899f21e1fe7", default-features = false }
|
||||||
qrcodegen = "1.8"
|
qrcodegen = "1.8"
|
||||||
|
lzma-rust2 = { version = "0.16", default-features = false, features = ["std", "encoder", "optimization", "xz"] }
|
||||||
|
scraper = "0.25"
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(target_arch, values("spirv"))'] }
|
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(target_arch, values("spirv"))'] }
|
||||||
|
|
@ -277,3 +280,4 @@ debug = true
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# Force cargo to use only one version of the dpi crate (vendoring breaks without this)
|
# Force cargo to use only one version of the dpi crate (vendoring breaks without this)
|
||||||
dpi = { git = "https://github.com/rust-windowing/winit.git" }
|
dpi = { git = "https://github.com/rust-windowing/winit.git" }
|
||||||
|
download-cef = { git = "https://github.com/timon-schelling/cef-rs.git", branch = "graphite" }
|
||||||
|
|
|
||||||
27
about.hbs
27
about.hbs
|
|
@ -1,27 +0,0 @@
|
||||||
{{!
|
|
||||||
Be careful to prevent auto-formatting from breaking this file's indentation.
|
|
||||||
Replace this file with JSON output once this is resolved: https://github.com/EmbarkStudios/cargo-about/issues/73
|
|
||||||
|
|
||||||
The `GENERATED_BY_CARGO_ABOUT` prefix is a JS labeled statement
|
|
||||||
(<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label>)
|
|
||||||
used so the reader of the generated file can verify the file does indeed start with that string,
|
|
||||||
while remaining valid JS for subsequent parsing.
|
|
||||||
}}
|
|
||||||
GENERATED_BY_CARGO_ABOUT: [
|
|
||||||
{{#each licenses}}
|
|
||||||
{
|
|
||||||
licenseName: `{{name}}`,
|
|
||||||
licenseText: `{{text}}`,
|
|
||||||
packages: [
|
|
||||||
{{#each used_by}}
|
|
||||||
{
|
|
||||||
name: `{{crate.name}}`,
|
|
||||||
version: `{{crate.version}}`,
|
|
||||||
author: `{{crate.authors}}`,
|
|
||||||
repository: `{{crate.repository}}`,
|
|
||||||
},
|
|
||||||
{{/each}}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{{/each}}
|
|
||||||
]
|
|
||||||
|
|
@ -44,8 +44,9 @@ vello = { workspace = true }
|
||||||
derivative = { workspace = true }
|
derivative = { workspace = true }
|
||||||
rfd = { workspace = true }
|
rfd = { workspace = true }
|
||||||
open = { workspace = true }
|
open = { workspace = true }
|
||||||
rand = { workspace = true, features = ["thread_rng"] }
|
lzma-rust2 = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
rand = { workspace = true, features = ["thread_rng"] }
|
||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
fd-lock = "4.0.4"
|
fd-lock = "4.0.4"
|
||||||
ctrlc = "3.5.1"
|
ctrlc = "3.5.1"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rfd::AsyncFileDialog;
|
use rfd::AsyncFileDialog;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::io::Read;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::mpsc::{Receiver, Sender, SyncSender};
|
use std::sync::mpsc::{Receiver, Sender, SyncSender};
|
||||||
|
|
@ -416,6 +417,18 @@ impl App {
|
||||||
DesktopFrontendMessage::Restart => {
|
DesktopFrontendMessage::Restart => {
|
||||||
self.exit(Some(ExitReason::Restart));
|
self.exit(Some(ExitReason::Restart));
|
||||||
}
|
}
|
||||||
|
DesktopFrontendMessage::LoadThirdPartyLicenses => {
|
||||||
|
let compressed = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/third-party-licenses.txt.xz"));
|
||||||
|
let mut reader = lzma_rust2::XzReader::new(compressed.as_slice(), false);
|
||||||
|
let mut text = String::new();
|
||||||
|
if let Err(e) = reader.read_to_string(&mut text) {
|
||||||
|
tracing::error!("Failed to decompress third-party licenses: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let message = DesktopWrapperMessage::LoadThirdPartyLicenses { text };
|
||||||
|
responses.push(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,5 +97,9 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
|
||||||
let message = AppWindowMessage::PointerLockMove { x, y };
|
let message = AppWindowMessage::PointerLockMove { x, y };
|
||||||
dispatcher.queue_editor_message(message);
|
dispatcher.queue_editor_message(message);
|
||||||
}
|
}
|
||||||
|
DesktopWrapperMessage::LoadThirdPartyLicenses { text } => {
|
||||||
|
let message = DialogMessage::RequestLicensesThirdPartyDialogWithLicenseText { license_text: text };
|
||||||
|
dispatcher.queue_editor_message(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,9 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
|
||||||
FrontendMessage::WindowRestart => {
|
FrontendMessage::WindowRestart => {
|
||||||
dispatcher.respond(DesktopFrontendMessage::Restart);
|
dispatcher.respond(DesktopFrontendMessage::Restart);
|
||||||
}
|
}
|
||||||
|
FrontendMessage::TriggerDisplayThirdPartyLicensesDialog => {
|
||||||
|
dispatcher.respond(DesktopFrontendMessage::LoadThirdPartyLicenses);
|
||||||
|
}
|
||||||
m => return Some(m),
|
m => return Some(m),
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ pub enum DesktopFrontendMessage {
|
||||||
WindowHideOthers,
|
WindowHideOthers,
|
||||||
WindowShowAll,
|
WindowShowAll,
|
||||||
Restart,
|
Restart,
|
||||||
|
LoadThirdPartyLicenses,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum DesktopWrapperMessage {
|
pub enum DesktopWrapperMessage {
|
||||||
|
|
@ -126,6 +127,9 @@ pub enum DesktopWrapperMessage {
|
||||||
x: f64,
|
x: f64,
|
||||||
y: f64,
|
y: f64,
|
||||||
},
|
},
|
||||||
|
LoadThirdPartyLicenses {
|
||||||
|
text: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug)]
|
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug)]
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -21,8 +21,8 @@
|
||||||
"lint-fix": "eslint . --fix && tsc --noEmit",
|
"lint-fix": "eslint . --fix && tsc --noEmit",
|
||||||
"---------- INTERNAL ----------": "",
|
"---------- INTERNAL ----------": "",
|
||||||
"setup": "node package-installer.js && node branding-installer.js",
|
"setup": "node package-installer.js && node branding-installer.js",
|
||||||
"native:build-dev": "wasm-pack build ./wasm --dev --target=web --no-default-features --features native && vite build --mode dev",
|
"native:build-dev": "wasm-pack build ./wasm --dev --target=web --no-default-features --features native && vite build --mode native",
|
||||||
"native:build-production": "wasm-pack build ./wasm --release --target=web --no-default-features --features native && vite build",
|
"native:build-production": "wasm-pack build ./wasm --release --target=web --no-default-features --features native && vite build --mode native",
|
||||||
"wasm:build-dev": "wasm-pack build ./wasm --dev --target=web",
|
"wasm:build-dev": "wasm-pack build ./wasm --dev --target=web",
|
||||||
"wasm:build-profiling": "wasm-pack build ./wasm --profiling --target=web",
|
"wasm:build-profiling": "wasm-pack build ./wasm --profiling --target=web",
|
||||||
"wasm:build-production": "wasm-pack build ./wasm --release --target=web",
|
"wasm:build-production": "wasm-pack build ./wasm --release --target=web",
|
||||||
|
|
@ -51,20 +51,20 @@
|
||||||
"eslint-plugin-prettier": "^5.5.5",
|
"eslint-plugin-prettier": "^5.5.5",
|
||||||
"eslint-plugin-svelte": "^3.14.0",
|
"eslint-plugin-svelte": "^3.14.0",
|
||||||
"globals": "^17.0.0",
|
"globals": "^17.0.0",
|
||||||
|
"license-checker-rseidelsohn": "^4.4.2",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"prettier": "^3.8.0",
|
|
||||||
"prettier-plugin-svelte": "^3.4.1",
|
"prettier-plugin-svelte": "^3.4.1",
|
||||||
|
"prettier": "^3.8.0",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"rollup-plugin-license": "^3.6.0",
|
|
||||||
"sass": "^1.97.2",
|
"sass": "^1.97.2",
|
||||||
"svelte": "5.47.1",
|
|
||||||
"svelte-preprocess": "^6.0.3",
|
"svelte-preprocess": "^6.0.3",
|
||||||
|
"svelte": "5.47.1",
|
||||||
"tar": "^7.5.4",
|
"tar": "^7.5.4",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.9.3",
|
|
||||||
"typescript-eslint": "^8.53.1",
|
"typescript-eslint": "^8.53.1",
|
||||||
"vite": "^7.3.1",
|
"typescript": "^5.9.3",
|
||||||
"vite-multiple-assets": "2.2.6"
|
"vite-multiple-assets": "2.2.6",
|
||||||
|
"vite": "^7.3.1"
|
||||||
},
|
},
|
||||||
"homepage": "https://graphite.art",
|
"homepage": "https://graphite.art",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,6 @@ export function createDialogState(editor: Editor) {
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerDisplayThirdPartyLicensesDialog, async () => {
|
editor.subscriptions.subscribeJsMessage(TriggerDisplayThirdPartyLicensesDialog, async () => {
|
||||||
const BACKUP_URL = "https://editor.graphite.art/third-party-licenses.txt";
|
const BACKUP_URL = "https://editor.graphite.art/third-party-licenses.txt";
|
||||||
let licenseText = `Content was not able to load. Please check your network connection and try again.\n\nOr visit ${BACKUP_URL} for the license notices.`;
|
let licenseText = `Content was not able to load. Please check your network connection and try again.\n\nOr visit ${BACKUP_URL} for the license notices.`;
|
||||||
if (editor.handle.inDevelopmentMode()) licenseText = `Third-party licenses are not available in development builds.\n\nVisit ${BACKUP_URL} for the license notices.`;
|
|
||||||
|
|
||||||
const response = await fetch("/third-party-licenses.txt");
|
const response = await fetch("/third-party-licenses.txt");
|
||||||
if (response.ok && response.headers.get("Content-Type")?.includes("text/plain")) licenseText = await response.text();
|
if (response.ok && response.headers.get("Content-Type")?.includes("text/plain")) licenseText = await response.text();
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
/* eslint-disable no-console */
|
import { execSync } from "child_process";
|
||||||
|
import { readFileSync } from "fs";
|
||||||
import { spawnSync } from "child_process";
|
|
||||||
import fs from "fs";
|
|
||||||
import os from "os";
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
||||||
import rollupPluginLicense, { type Dependency } from "rollup-plugin-license";
|
|
||||||
import { sveltePreprocess } from "svelte-preprocess";
|
import { sveltePreprocess } from "svelte-preprocess";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig, type PluginOption } from "vite";
|
||||||
import { DynamicPublicDirectory as viteMultipleAssets } from "vite-multiple-assets";
|
import { DynamicPublicDirectory as viteMultipleAssets } from "vite-multiple-assets";
|
||||||
|
|
||||||
const projectRootDir = path.resolve(__dirname);
|
const projectRootDir = path.resolve(__dirname);
|
||||||
|
|
@ -16,7 +12,24 @@ const projectRootDir = path.resolve(__dirname);
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
return {
|
return {
|
||||||
plugins: [
|
plugins: plugins(mode),
|
||||||
|
resolve: {
|
||||||
|
alias: [
|
||||||
|
{ find: /@branding\/(.*\.svg)/, replacement: path.resolve(projectRootDir, "../branding", "$1?raw") },
|
||||||
|
{ find: "@graphite/../assets", replacement: path.resolve(projectRootDir, "assets") },
|
||||||
|
{ find: "@graphite/../public", replacement: path.resolve(projectRootDir, "public") },
|
||||||
|
{ find: "@graphite", replacement: path.resolve(projectRootDir, "src") },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 8080,
|
||||||
|
host: "0.0.0.0",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function plugins(mode: string): PluginOption[] {
|
||||||
|
const plugins = [
|
||||||
svelte({
|
svelte({
|
||||||
preprocess: [sveltePreprocess()],
|
preprocess: [sveltePreprocess()],
|
||||||
onwarn(warning, defaultHandler) {
|
onwarn(warning, defaultHandler) {
|
||||||
|
|
@ -45,387 +58,30 @@ export default defineConfig(({ mode }) => {
|
||||||
// Options where we set custom MIME types
|
// Options where we set custom MIME types
|
||||||
{ mimeTypes: { ".graphite": "application/json" } },
|
{ mimeTypes: { ".graphite": "application/json" } },
|
||||||
),
|
),
|
||||||
],
|
|
||||||
resolve: {
|
|
||||||
alias: [
|
|
||||||
{ find: /@branding\/(.*\.svg)/, replacement: path.resolve(projectRootDir, "../branding", "$1?raw") },
|
|
||||||
{ find: "@graphite/../assets", replacement: path.resolve(projectRootDir, "assets") },
|
|
||||||
{ find: "@graphite/../public", replacement: path.resolve(projectRootDir, "public") },
|
|
||||||
{ find: "@graphite", replacement: path.resolve(projectRootDir, "src") },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
port: 8080,
|
|
||||||
host: "0.0.0.0",
|
|
||||||
},
|
|
||||||
build: {
|
|
||||||
rollupOptions: {
|
|
||||||
plugins:
|
|
||||||
mode !== "dev"
|
|
||||||
? [
|
|
||||||
rollupPluginLicense({
|
|
||||||
thirdParty: {
|
|
||||||
includePrivate: false,
|
|
||||||
multipleVersions: true,
|
|
||||||
allow: {
|
|
||||||
test: `(${getAcceptedLicenses()})`,
|
|
||||||
failOnUnlicensed: true,
|
|
||||||
failOnViolation: true,
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
file: path.resolve(__dirname, "./dist/third-party-licenses.txt"),
|
|
||||||
template: formatThirdPartyLicenses,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
type LicenseInfo = {
|
|
||||||
licenseName: string;
|
|
||||||
licenseText: string;
|
|
||||||
noticeText?: string;
|
|
||||||
packages: PackageInfo[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type PackageInfo = {
|
|
||||||
name: string;
|
|
||||||
version: string;
|
|
||||||
author: string;
|
|
||||||
repository: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
function formatThirdPartyLicenses(jsLicenses: Dependency[]): string {
|
|
||||||
// Generate the Rust license information.
|
|
||||||
const rustLicenses = generateRustLicenses();
|
|
||||||
const additionalLicenses = generateAdditionalLicenses();
|
|
||||||
|
|
||||||
// Ensure we have the required license information to work with before proceeding.
|
|
||||||
if (rustLicenses.length === 0) {
|
|
||||||
// This is probably caused by `cargo about` not being installed.
|
|
||||||
console.error("Could not run `cargo about`, which is required to generate license information.");
|
|
||||||
console.error("To install cargo-about on your system, you can run `cargo install cargo-about`.");
|
|
||||||
console.error("License information is required in production builds. Aborting.");
|
|
||||||
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
if (jsLicenses.length === 0) {
|
|
||||||
console.error("No JavaScript package licenses were found by `rollup-plugin-license`. Please investigate.");
|
|
||||||
console.error("License information is required in production builds. Aborting.");
|
|
||||||
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let licenses = rustLicenses.concat(additionalLicenses);
|
|
||||||
|
|
||||||
// SPECIAL CASE: Find then duplicate this license if one of its packages is `path-bool`, adding its notice text.
|
|
||||||
let foundLicensesIndex: number | undefined = undefined;
|
|
||||||
let foundPackagesIndex: number | undefined = undefined;
|
|
||||||
licenses.forEach((license, licenseIndex) => {
|
|
||||||
license.packages.forEach((pkg, pkgIndex) => {
|
|
||||||
if (pkg.name === "path-bool") {
|
|
||||||
foundLicensesIndex = licenseIndex;
|
|
||||||
foundPackagesIndex = pkgIndex;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (foundLicensesIndex !== undefined && foundPackagesIndex !== undefined) {
|
|
||||||
const license = licenses[foundLicensesIndex];
|
|
||||||
const pkg = license.packages[foundPackagesIndex];
|
|
||||||
|
|
||||||
license.packages = license.packages.filter((pkg) => pkg.name !== "path-bool");
|
|
||||||
const noticeText = fs.readFileSync(path.resolve(__dirname, "../libraries/path-bool/NOTICE"), "utf8");
|
|
||||||
|
|
||||||
licenses.push({
|
|
||||||
licenseName: license.licenseName,
|
|
||||||
licenseText: license.licenseText,
|
|
||||||
noticeText,
|
|
||||||
packages: [pkg],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extend the license list with the provided JS licenses.
|
|
||||||
jsLicenses.forEach((jsLicense) => {
|
|
||||||
const name = jsLicense.name || "";
|
|
||||||
const version = jsLicense.version || "";
|
|
||||||
const author = jsLicense.author?.text() || "";
|
|
||||||
const licenseName = jsLicense.license || "";
|
|
||||||
const licenseText = trimBlankLines(jsLicense.licenseText || "");
|
|
||||||
const noticeText = trimBlankLines(jsLicense.noticeText || "");
|
|
||||||
|
|
||||||
let repository = jsLicense.repository || "";
|
|
||||||
if (repository && typeof repository === "object") repository = repository.url;
|
|
||||||
|
|
||||||
const matchedLicense = licenses.find(
|
|
||||||
(license) => license.licenseName === licenseName && trimBlankLines(license.licenseText || "") === licenseText && trimBlankLines(license.noticeText || "") === noticeText,
|
|
||||||
);
|
|
||||||
|
|
||||||
const pkg: PackageInfo = { name, version, author, repository };
|
|
||||||
if (matchedLicense) matchedLicense.packages.push(pkg);
|
|
||||||
else licenses.push({ licenseName, licenseText, noticeText, packages: [pkg] });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Combine any license notices into the license text.
|
|
||||||
licenses.forEach((license, index) => {
|
|
||||||
if (license.noticeText) {
|
|
||||||
licenses[index].licenseText += "\n\n";
|
|
||||||
licenses[index].licenseText += " _______________________________________\n";
|
|
||||||
licenses[index].licenseText += "│ │\n";
|
|
||||||
licenses[index].licenseText += "│ THE FOLLOWING NOTICE FILE IS INCLUDED │\n";
|
|
||||||
licenses[index].licenseText += "│ │\n";
|
|
||||||
licenses[index].licenseText += " ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\n\n";
|
|
||||||
licenses[index].licenseText += `${license.noticeText}\n`;
|
|
||||||
licenses[index].noticeText = undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// De-duplicate any licenses with the same text by merging their lists of packages.
|
|
||||||
const licensesNormalizedWhitespace = licenses.map((license) => license.licenseText.replace(/[\n\s]+/g, " ").trim());
|
|
||||||
licenses.forEach((currentLicense, currentLicenseIndex) => {
|
|
||||||
licenses.slice(0, currentLicenseIndex).forEach((comparisonLicense, comparisonLicenseIndex) => {
|
|
||||||
if (licensesNormalizedWhitespace[currentLicenseIndex] === licensesNormalizedWhitespace[comparisonLicenseIndex]) {
|
|
||||||
currentLicense.packages.push(...comparisonLicense.packages);
|
|
||||||
comparisonLicense.packages = [];
|
|
||||||
// After emptying the packages, the redundant license with no packages will be removed in the next step's `filter()`.
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Filter out first-party internal Graphite crates.
|
|
||||||
licenses = licenses.filter((license) => {
|
|
||||||
license.packages = license.packages.filter(
|
|
||||||
(packageInfo) =>
|
|
||||||
!(packageInfo.repository && packageInfo.repository.toLowerCase().includes("github.com/GraphiteEditor/Graphite".toLowerCase())) &&
|
|
||||||
!(
|
|
||||||
packageInfo.author &&
|
|
||||||
packageInfo.author.toLowerCase().includes("contact@graphite.art") &&
|
|
||||||
// Exclude a comma which indicates multiple authors, which we need to not filter out
|
|
||||||
!packageInfo.author.toLowerCase().includes(",")
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return license.packages.length > 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sort the licenses by the number of packages using the same license, and then alphabetically by license name.
|
|
||||||
licenses.sort((a, b) => a.licenseText.localeCompare(b.licenseText));
|
|
||||||
licenses.sort((a, b) => a.licenseName.localeCompare(b.licenseName));
|
|
||||||
licenses.sort((a, b) => b.packages.length - a.packages.length);
|
|
||||||
// Sort the individual packages using each license alphabetically.
|
|
||||||
licenses.forEach((license) => {
|
|
||||||
license.packages.sort((a, b) => a.name.localeCompare(b.name));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Prepare a header for the license notice.
|
|
||||||
let formattedLicenseNotice = "";
|
|
||||||
formattedLicenseNotice += "▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐\n";
|
|
||||||
formattedLicenseNotice += "▐▐ ▐▐\n";
|
|
||||||
formattedLicenseNotice += "▐▐ GRAPHITE THIRD-PARTY SOFTWARE LICENSE NOTICES ▐▐\n";
|
|
||||||
formattedLicenseNotice += "▐▐ ▐▐\n";
|
|
||||||
formattedLicenseNotice += "▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐\n";
|
|
||||||
|
|
||||||
// Append a block for each license shared by multiple packages with identical license text.
|
|
||||||
licenses.forEach((license) => {
|
|
||||||
let packagesWithSameLicense = license.packages.map((packageInfo) => {
|
|
||||||
const { name, version, author, repository } = packageInfo;
|
|
||||||
|
|
||||||
// Remove the `git+` or `git://` prefix and `.git` suffix.
|
|
||||||
let repo = repository;
|
|
||||||
if (repo.startsWith("git+")) repo = repo.slice("git+".length);
|
|
||||||
if (repo.startsWith("git://")) repo = repo.slice("git://".length);
|
|
||||||
if (repo.endsWith(".git")) repo = repo.slice(0, -".git".length);
|
|
||||||
if (repo.endsWith(".git#release")) repo = repo.slice(0, -".git#release".length);
|
|
||||||
|
|
||||||
return `${name} ${version}${author ? ` - ${author}` : ""}${repo ? ` - ${repo}` : ""}`;
|
|
||||||
});
|
|
||||||
const multi = packagesWithSameLicense.length !== 1;
|
|
||||||
const saysLicense = license.licenseName.toLowerCase().includes("license");
|
|
||||||
const header = `The package${multi ? "s" : ""} listed here ${multi ? "are" : "is"} licensed under the terms of the ${license.licenseName}${saysLicense ? "" : " license"} printed beneath`;
|
|
||||||
const packagesLineLength = Math.max(header.length, ...packagesWithSameLicense.map((line) => line.length));
|
|
||||||
packagesWithSameLicense = packagesWithSameLicense.map((line) => `│ ${line}${" ".repeat(packagesLineLength - line.length)} │`);
|
|
||||||
|
|
||||||
formattedLicenseNotice += "\n";
|
|
||||||
formattedLicenseNotice += ` ${"_".repeat(packagesLineLength + 2)}\n`;
|
|
||||||
formattedLicenseNotice += `│ ${" ".repeat(packagesLineLength)} │\n`;
|
|
||||||
formattedLicenseNotice += `│ ${header}${" ".repeat(packagesLineLength - header.length)} │\n`;
|
|
||||||
formattedLicenseNotice += `│${"_".repeat(packagesLineLength + 2)}│\n`;
|
|
||||||
formattedLicenseNotice += `${packagesWithSameLicense.join("\n")}\n`;
|
|
||||||
formattedLicenseNotice += ` ${"‾".repeat(packagesLineLength + 2)}\n`;
|
|
||||||
formattedLicenseNotice += `${license.licenseText}\n`;
|
|
||||||
});
|
|
||||||
|
|
||||||
formattedLicenseNotice += "\n";
|
|
||||||
return formattedLicenseNotice;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include additional licenses that aren't automatically generated by `cargo about` or `rollup-plugin-license`.
|
|
||||||
function generateAdditionalLicenses(): LicenseInfo[] {
|
|
||||||
const ADDITIONAL_LICENSES = [
|
|
||||||
{
|
|
||||||
licenseName: "SIL Open Font License 1.1",
|
|
||||||
licenseTextPath: "node_modules/source-sans-pro/LICENSE.txt",
|
|
||||||
manifestPath: "node_modules/source-sans-pro/package.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
licenseName: "SIL Open Font License 1.1",
|
|
||||||
licenseTextPath: "node_modules/source-code-pro/LICENSE.md",
|
|
||||||
manifestPath: "node_modules/source-code-pro/package.json",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return ADDITIONAL_LICENSES.map(({ licenseName, licenseTextPath, manifestPath }) => {
|
if (mode !== "native") {
|
||||||
const licenseText = (fs.existsSync(licenseTextPath) && fs.readFileSync(licenseTextPath, "utf8")) || "";
|
plugins.push({
|
||||||
|
name: "third-party-licenses",
|
||||||
const manifestJSON = (fs.existsSync(manifestPath) && JSON.parse(fs.readFileSync(manifestPath, "utf8"))) || {};
|
buildStart() {
|
||||||
const name = manifestJSON.name || "";
|
|
||||||
const version = manifestJSON.version || "";
|
|
||||||
const author = manifestJSON.author.name || manifestJSON.author || "";
|
|
||||||
const repository = manifestJSON.repository?.url || "";
|
|
||||||
|
|
||||||
return {
|
|
||||||
licenseName,
|
|
||||||
licenseText: trimBlankLines(licenseText),
|
|
||||||
packages: [{ name, version, author, repository }],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateRustLicenses(): LicenseInfo[] {
|
|
||||||
// Log the starting status to the build output.
|
|
||||||
console.info("\n\nGenerating license information for Rust code\n");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Call `cargo about` in the terminal to generate the license information for Rust crates.
|
execSync("cargo run -p third-party-licenses", {
|
||||||
// The `about.hbs` file is written so it generates a valid JavaScript array expression which we evaluate below.
|
stdio: "inherit",
|
||||||
const { licenses, status, stderr } = (() => {
|
|
||||||
// On Windows, we have to write the output to a temporary file because of powershell's handling of stdout.
|
|
||||||
if (os.platform() === "win32") {
|
|
||||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "graphite-licenses-"));
|
|
||||||
const licensesFile = path.join(tmpDir, "licenses.js");
|
|
||||||
|
|
||||||
const { status, stderr } = spawnSync("cargo", ["about", "generate", "about.hbs", "-o", licensesFile], {
|
|
||||||
cwd: path.join(__dirname, ".."),
|
|
||||||
encoding: "utf8",
|
|
||||||
shell: true,
|
|
||||||
windowsHide: true, // Hide the terminal on Windows
|
|
||||||
});
|
});
|
||||||
|
} catch (_e) {
|
||||||
const licenses = fs.existsSync(licensesFile) ? fs.readFileSync(licensesFile, "utf8") : "";
|
this.error("Failed to generate third-party licenses");
|
||||||
|
}
|
||||||
return { licenses, status, stderr };
|
},
|
||||||
} else {
|
generateBundle() {
|
||||||
const { stdout, status, stderr } = spawnSync("cargo", ["about", "generate", "about.hbs"], {
|
const source = readFileSync(path.resolve(projectRootDir, "third-party-licenses.txt"), "utf-8");
|
||||||
cwd: path.join(__dirname, ".."),
|
this.emitFile({
|
||||||
encoding: "utf8",
|
type: "asset",
|
||||||
shell: true,
|
fileName: "third-party-licenses.txt",
|
||||||
|
source,
|
||||||
});
|
});
|
||||||
|
},
|
||||||
return { licenses: stdout, status, stderr };
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
// If the command failed, print the error message and exit early.
|
|
||||||
if (status !== 0) {
|
|
||||||
// Cargo returns 101 when the subcommand (`about`) wasn't found, so we skip printing the below error message in that case.
|
|
||||||
if (status !== 101) {
|
|
||||||
console.error("cargo-about failed", status, stderr);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the output starts with this expected label, which lets us know the file generated with expected output.
|
|
||||||
// We don't want to eval an error message or something else, so we fail early if that happens.
|
|
||||||
if (!licenses.trim().startsWith("GENERATED_BY_CARGO_ABOUT:")) {
|
|
||||||
console.error("Unexpected output from cargo-about", licenses);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the array JS syntax string into an actual JS array in memory.
|
|
||||||
// Security-wise, eval() isn't any worse than require(), but it's able to work without a temporary file.
|
|
||||||
// We call eval indirectly to avoid a warning as explained here: <https://esbuild.github.io/content-types/#direct-eval>.
|
|
||||||
const indirectEval = eval;
|
|
||||||
const licensesArray = indirectEval(licenses) as LicenseInfo[];
|
|
||||||
|
|
||||||
// Remove the HTML character encoding caused by Handlebars.
|
|
||||||
const rustLicenses = (licensesArray || []).map(
|
|
||||||
(rustLicense): LicenseInfo => ({
|
|
||||||
licenseName: htmlDecode(rustLicense.licenseName),
|
|
||||||
licenseText: trimBlankLines(htmlDecode(rustLicense.licenseText)),
|
|
||||||
packages: rustLicense.packages.map(
|
|
||||||
(packageInfo): PackageInfo => ({
|
|
||||||
name: htmlDecode(packageInfo.name),
|
|
||||||
version: htmlDecode(packageInfo.version),
|
|
||||||
author: htmlDecode(packageInfo.author)
|
|
||||||
.replace(/\[(.*), \]/, "$1")
|
|
||||||
.replace("[]", ""),
|
|
||||||
repository: htmlDecode(packageInfo.repository),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
return rustLicenses;
|
|
||||||
} catch (_) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function htmlDecode(input: string): string {
|
|
||||||
if (!input) return input;
|
|
||||||
|
|
||||||
const htmlEntities = {
|
|
||||||
nbsp: " ",
|
|
||||||
copy: "©",
|
|
||||||
reg: "®",
|
|
||||||
lt: "<",
|
|
||||||
gt: ">",
|
|
||||||
amp: "&",
|
|
||||||
apos: "'",
|
|
||||||
quot: `"`,
|
|
||||||
};
|
|
||||||
|
|
||||||
return input.replace(/&([^;]+);/g, (entity: string, entityCode: string) => {
|
|
||||||
const maybeEntity = Object.entries(htmlEntities).find(([key, _]) => key === entityCode);
|
|
||||||
if (maybeEntity) return maybeEntity[1];
|
|
||||||
|
|
||||||
let match;
|
|
||||||
if ((match = entityCode.match(/^#x([\da-fA-F]+)$/))) {
|
|
||||||
return String.fromCharCode(parseInt(match[1], 16));
|
|
||||||
}
|
|
||||||
if ((match = entityCode.match(/^#(\d+)$/))) {
|
|
||||||
return String.fromCharCode(~~match[1]);
|
|
||||||
}
|
|
||||||
return entity;
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function trimBlankLines(input: string): string {
|
|
||||||
let result = input.replace(/\r/g, "");
|
|
||||||
|
|
||||||
while (result.charAt(0) === "\r" || result.charAt(0) === "\n") {
|
|
||||||
result = result.slice(1);
|
|
||||||
}
|
|
||||||
while (result.slice(-1) === "\r" || result.slice(-1) === "\n") {
|
|
||||||
result = result.slice(0, -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return plugins;
|
||||||
}
|
|
||||||
|
|
||||||
function getAcceptedLicenses() {
|
|
||||||
const tomlContent = fs.readFileSync(path.resolve(__dirname, "../about.toml"), "utf8");
|
|
||||||
|
|
||||||
const licensesBlock = tomlContent?.match(/accepted\s*=\s*\[([^\]]*)\]/)?.[1] || "";
|
|
||||||
|
|
||||||
return licensesBlock
|
|
||||||
.split("\n")
|
|
||||||
.map((line) => line.replace(/#.*$/, "")) // Remove comments
|
|
||||||
.join("\n")
|
|
||||||
.split(",")
|
|
||||||
.map((license) => license.trim().replace(/"/g, ""))
|
|
||||||
.filter((license) => license.length > 0)
|
|
||||||
.join(" OR ");
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ name = "graph-craft"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["dealloc_nodes", "wgpu"]
|
default = ["dealloc_nodes", "wgpu"]
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ name = "interpreted-executor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|
@ -56,4 +57,3 @@ harness = false
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "run_cached_iai"
|
name = "run_cached_iai"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ name = "wgpu-executor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Local dependencies
|
# Local dependencies
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ name = "graphic-nodes"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Local dependencies
|
# Local dependencies
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ name = "preprocessor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,15 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"---------- DEV SERVER ----------": "",
|
"---------- DEV SERVER ----------": "",
|
||||||
"start": "cd frontend && npm start",
|
"start": "cd frontend && npm start",
|
||||||
"start-desktop": "cd frontend && npm run build-native-dev && cargo run -p graphite-desktop-bundle -- open",
|
"start-desktop": "cd frontend && npm run build-native-dev && cargo run -p third-party-licenses --features desktop && cargo run -p graphite-desktop-bundle -- open",
|
||||||
"profiling": "cd frontend && npm run profiling",
|
"profiling": "cd frontend && npm run profiling",
|
||||||
"production": "cd frontend && npm run production",
|
"production": "cd frontend && npm run production",
|
||||||
"---------- BUILDS ----------": "",
|
"---------- BUILDS ----------": "",
|
||||||
"build-dev": "cd frontend && npm run build-dev",
|
"build-dev": "cd frontend && npm run build-dev",
|
||||||
"build-profiling": "cd frontend && npm run build-profiling",
|
"build-profiling": "cd frontend && npm run build-profiling",
|
||||||
"build": "cd frontend && npm run build",
|
"build": "cd frontend && npm run build",
|
||||||
"build-desktop": "cd frontend && npm run build-native && cargo run -r -p graphite-desktop-bundle",
|
"build-desktop": "cd frontend && npm run build-native && cargo run -p third-party-licenses --features desktop && cargo run -r -p graphite-desktop-bundle",
|
||||||
"build-desktop-dev": "cd frontend && npm run build-native-dev && cargo run -p graphite-desktop-bundle",
|
"build-desktop-dev": "cd frontend && npm run build-native-dev && cargo run -p third-party-licenses --features desktop && cargo run -p graphite-desktop-bundle",
|
||||||
"---------- UTILITIES ----------": "",
|
"---------- UTILITIES ----------": "",
|
||||||
"lint": "cd frontend && npm run lint",
|
"lint": "cd frontend && npm run lint",
|
||||||
"lint-fix": "cd frontend && npm run lint-fix"
|
"lint-fix": "cd frontend && npm run lint-fix"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
*.hash
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
[package]
|
||||||
|
name = "third-party-licenses"
|
||||||
|
edition.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
desktop = ["dep:cef-dll-sys", "dep:scraper"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Workspace dependencies
|
||||||
|
serde = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
lzma-rust2 = { workspace = true }
|
||||||
|
|
||||||
|
# Optional workspace dependencies
|
||||||
|
cef-dll-sys = { workspace = true, optional = true }
|
||||||
|
scraper = { workspace = true, optional = true }
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
fn main() {
|
||||||
|
println!("cargo:rerun-if-changed=src");
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-env-changed=DEP_CEF_DLL_WRAPPER_CEF_DIR");
|
||||||
|
if let Ok(cef_dir) = std::env::var("DEP_CEF_DLL_WRAPPER_CEF_DIR") {
|
||||||
|
println!("cargo:rustc-env=CEF_PATH={cef_dir}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
if std::env::var("CARGO_FEATURE_DESKTOP").is_ok() {
|
||||||
|
let _ = std::fs::remove_file(manifest_dir.join("desktop.hash"));
|
||||||
|
} else {
|
||||||
|
let _ = std::fs::remove_file(manifest_dir.join("web.hash"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
use crate::{LicenceSource, LicenseEntry, Package};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::fs;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::{self, Command};
|
||||||
|
|
||||||
|
pub struct CargoLicenseSource {}
|
||||||
|
|
||||||
|
impl CargoLicenseSource {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LicenceSource for CargoLicenseSource {
|
||||||
|
fn licenses(&self) -> Vec<LicenseEntry> {
|
||||||
|
parse(run())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for CargoLicenseSource {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
let lock_path = PathBuf::from(env!("CARGO_WORKSPACE_DIR")).join("Cargo.lock");
|
||||||
|
fs::read_to_string(lock_path).unwrap().hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Output {
|
||||||
|
licenses: Vec<License>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct License {
|
||||||
|
name: Option<String>,
|
||||||
|
text: Option<String>,
|
||||||
|
used_by: Vec<UsedBy>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct UsedBy {
|
||||||
|
#[serde(rename = "crate")]
|
||||||
|
crate_info: Crate,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Crate {
|
||||||
|
name: Option<String>,
|
||||||
|
version: Option<String>,
|
||||||
|
authors: Option<Vec<String>>,
|
||||||
|
repository: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(parsed: Output) -> Vec<LicenseEntry> {
|
||||||
|
parsed
|
||||||
|
.licenses
|
||||||
|
.into_iter()
|
||||||
|
.map(|license| {
|
||||||
|
let packages = license
|
||||||
|
.used_by
|
||||||
|
.into_iter()
|
||||||
|
.map(|used| {
|
||||||
|
let name = used.crate_info.name.as_deref().unwrap_or_default();
|
||||||
|
let version = used.crate_info.version.as_deref().unwrap_or_default();
|
||||||
|
let display_name = if version.is_empty() { name.to_string() } else { format!("{name}@{version}") };
|
||||||
|
|
||||||
|
let repository = used.crate_info.repository.filter(|s| !s.is_empty());
|
||||||
|
|
||||||
|
Package {
|
||||||
|
name: display_name,
|
||||||
|
authors: used.crate_info.authors.unwrap_or_default(),
|
||||||
|
url: repository,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
LicenseEntry {
|
||||||
|
name: license.name,
|
||||||
|
text: license.text.as_deref().unwrap_or_default().to_string(),
|
||||||
|
packages,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run() -> Output {
|
||||||
|
let output = Command::new("cargo")
|
||||||
|
.args(["about", "generate", "--format", "json", "--frozen"])
|
||||||
|
.current_dir(env!("CARGO_WORKSPACE_DIR"))
|
||||||
|
.output()
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to run cargo about generate: {e}");
|
||||||
|
process::exit(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
eprintln!("cargo about generate failed:\n{}", String::from_utf8_lossy(&output.stderr));
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
serde_json::from_str(&String::from_utf8(output.stdout).expect("cargo about generate should return valid UTF-8")).unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to parse cargo about generate JSON: {e}");
|
||||||
|
process::exit(1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
use lzma_rust2::XzReader;
|
||||||
|
use scraper::{Html, Selector};
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{fs, process};
|
||||||
|
|
||||||
|
use crate::{LicenceSource, LicenseEntry, Package};
|
||||||
|
|
||||||
|
pub struct CefLicenseSource;
|
||||||
|
|
||||||
|
impl CefLicenseSource {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LicenceSource for CefLicenseSource {
|
||||||
|
fn licenses(&self) -> Vec<LicenseEntry> {
|
||||||
|
let html = read();
|
||||||
|
parse(&html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for CefLicenseSource {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
read().hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(html: &str) -> Vec<LicenseEntry> {
|
||||||
|
let document = Html::parse_document(html);
|
||||||
|
|
||||||
|
let product_sel = Selector::parse("div.product").unwrap();
|
||||||
|
let title_sel = Selector::parse("span.title").unwrap();
|
||||||
|
let homepage_sel = Selector::parse("span.homepage a").unwrap();
|
||||||
|
let license_sel = Selector::parse("div.license pre").unwrap();
|
||||||
|
|
||||||
|
document
|
||||||
|
.select(&product_sel)
|
||||||
|
.filter_map(|product| {
|
||||||
|
let name: String = product.select(&title_sel).next().map(|el| el.text().collect()).unwrap_or_default();
|
||||||
|
|
||||||
|
if name.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let homepage = product.select(&homepage_sel).next().and_then(|el| el.value().attr("href").map(String::from));
|
||||||
|
|
||||||
|
let license_text: String = product.select(&license_sel).next().map(|el| el.text().collect::<String>()).unwrap_or_default().trim().to_string();
|
||||||
|
|
||||||
|
let pkg = Package {
|
||||||
|
name,
|
||||||
|
url: homepage,
|
||||||
|
authors: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(LicenseEntry {
|
||||||
|
name: None,
|
||||||
|
text: license_text,
|
||||||
|
packages: vec![pkg],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read() -> String {
|
||||||
|
let cef_path = PathBuf::from(env!("CEF_PATH"));
|
||||||
|
let cef_credits = std::fs::read_dir(&cef_path)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to read CEF_PATH directory {}: {e}", cef_path.display());
|
||||||
|
process::exit(1);
|
||||||
|
})
|
||||||
|
.filter_map(|entry| entry.ok())
|
||||||
|
.find(|entry| {
|
||||||
|
let name = entry.file_name();
|
||||||
|
name.eq_ignore_ascii_case("credits.html") || name.eq_ignore_ascii_case("credits.html.xz")
|
||||||
|
})
|
||||||
|
.map(|entry| entry.path())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
eprintln!("Could not find CREDITS.html or CREDITS.html.xz in {}", cef_path.display());
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
let decompress_xz = cef_credits.extension().map(|ext| ext.eq_ignore_ascii_case("xz")).unwrap_or(false);
|
||||||
|
|
||||||
|
if decompress_xz {
|
||||||
|
let file = fs::File::open(&cef_credits).unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to open CEF credits file {}: {e}", cef_credits.display());
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
let mut reader = XzReader::new(file, false);
|
||||||
|
let mut html = String::new();
|
||||||
|
reader.read_to_string(&mut html).unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to decompress CEF credits file {}: {e}", cef_credits.display());
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
html
|
||||||
|
} else {
|
||||||
|
fs::read_to_string(&cef_credits).unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to read CEF credits file {}: {e}", cef_credits.display());
|
||||||
|
process::exit(1);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,229 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{fs, process};
|
||||||
|
|
||||||
|
mod cargo;
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
mod cef;
|
||||||
|
mod npm;
|
||||||
|
|
||||||
|
use crate::cargo::CargoLicenseSource;
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
use crate::cef::CefLicenseSource;
|
||||||
|
use crate::npm::NpmLicenseSource;
|
||||||
|
|
||||||
|
pub trait LicenceSource: std::hash::Hash {
|
||||||
|
fn licenses(&self) -> Vec<LicenseEntry>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LicenseEntry {
|
||||||
|
name: Option<String>,
|
||||||
|
text: String,
|
||||||
|
packages: Vec<Package>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Package {
|
||||||
|
name: String,
|
||||||
|
authors: Vec<String>,
|
||||||
|
url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash)]
|
||||||
|
struct Run<'a> {
|
||||||
|
output: &'a Vec<u8>,
|
||||||
|
cargo: &'a CargoLicenseSource,
|
||||||
|
npm: &'a NpmLicenseSource,
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
cef: &'a CefLicenseSource,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
let workspace_dir = PathBuf::from(env!("CARGO_WORKSPACE_DIR"));
|
||||||
|
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
let output_path = workspace_dir.join("desktop/third-party-licenses.txt.xz");
|
||||||
|
#[cfg(not(feature = "desktop"))]
|
||||||
|
let output_path = workspace_dir.join("frontend/third-party-licenses.txt");
|
||||||
|
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
let current_hash_path = manifest_dir.join("desktop.hash");
|
||||||
|
#[cfg(not(feature = "desktop"))]
|
||||||
|
let current_hash_path = manifest_dir.join("web.hash");
|
||||||
|
|
||||||
|
let cargo_source = CargoLicenseSource::new();
|
||||||
|
let npm_source = NpmLicenseSource::new(workspace_dir.join("frontend"));
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
let cef_source = CefLicenseSource::new();
|
||||||
|
|
||||||
|
let mut run = Run {
|
||||||
|
cargo: &cargo_source,
|
||||||
|
npm: &npm_source,
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
cef: &cef_source,
|
||||||
|
output: &fs::read(&output_path).unwrap_or_default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
run.hash(&mut hasher);
|
||||||
|
let current_hash = format!("{:016x}", hasher.finish());
|
||||||
|
|
||||||
|
if current_hash == fs::read_to_string(¤t_hash_path).unwrap_or_default() {
|
||||||
|
eprintln!("No changes in licenses detected, skipping generation.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
eprintln!("Changes in licenses detected, generating new license file.");
|
||||||
|
|
||||||
|
let licenses = merge_filter_dedup_and_sort(vec![
|
||||||
|
cargo_source.licenses(),
|
||||||
|
npm_source.licenses(),
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
cef_source.licenses(),
|
||||||
|
]);
|
||||||
|
let formatted = format_credits(&licenses);
|
||||||
|
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
let output = compress(&formatted);
|
||||||
|
#[cfg(not(feature = "desktop"))]
|
||||||
|
let output = formatted.as_bytes().to_vec();
|
||||||
|
if let Some(parent) = output_path.parent() {
|
||||||
|
fs::create_dir_all(parent).unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to create directory {}: {e}", parent.display());
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fs::write(&output_path, &output).unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to write {}: {e}", &output_path.display());
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
run.output = &output;
|
||||||
|
|
||||||
|
let hash = {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
run.hash(&mut hasher);
|
||||||
|
format!("{:016x}", hasher.finish())
|
||||||
|
};
|
||||||
|
|
||||||
|
fs::write(¤t_hash_path, hash).unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to write hash file {}: {e}", current_hash_path.display());
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_credits(licenses: &Vec<LicenseEntry>) -> String {
|
||||||
|
let mut out = String::new();
|
||||||
|
|
||||||
|
out.push_str("▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐\n");
|
||||||
|
out.push_str("▐▐ ▐▐\n");
|
||||||
|
out.push_str("▐▐ GRAPHITE THIRD-PARTY SOFTWARE LICENSE NOTICES ▐▐\n");
|
||||||
|
out.push_str("▐▐ ▐▐\n");
|
||||||
|
out.push_str("▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐\n");
|
||||||
|
|
||||||
|
for license in licenses {
|
||||||
|
let package_lines: Vec<String> = license
|
||||||
|
.packages
|
||||||
|
.iter()
|
||||||
|
.map(|pkg| match &pkg {
|
||||||
|
Package { name, authors, url: Some(url) } if !authors.is_empty() => format!("{} - [{}] - {}", name, authors.join(", "), url),
|
||||||
|
Package { name, authors: _, url: Some(url) } => format!("{} - {}", name, url),
|
||||||
|
Package { name, authors, url: None } if !authors.is_empty() => format!("{} - [{}]", name, authors.join(", ")),
|
||||||
|
_ => pkg.name.clone(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let multi = package_lines.len() > 1;
|
||||||
|
|
||||||
|
let header = format!(
|
||||||
|
"The package{} listed here {} licensed under the terms of the {} printed beneath",
|
||||||
|
if multi { "s" } else { "" },
|
||||||
|
if multi { "are" } else { "is" },
|
||||||
|
if let Some(license) = license.name.as_ref() { license.to_string() } else { "license".to_string() }
|
||||||
|
);
|
||||||
|
|
||||||
|
let max_len = std::iter::once(header.len()).chain(package_lines.iter().map(|l| l.chars().count())).max().unwrap_or(0);
|
||||||
|
|
||||||
|
let padded_packages: Vec<String> = package_lines
|
||||||
|
.iter()
|
||||||
|
.map(|line| {
|
||||||
|
let pad = max_len - line.chars().count();
|
||||||
|
format!("│ {}{} │", line, " ".repeat(pad))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
out.push_str(&format!("\n {}\n", "_".repeat(max_len + 2)));
|
||||||
|
out.push_str(&format!("│ {} │\n", " ".repeat(max_len)));
|
||||||
|
out.push_str(&format!("│ {}{} │\n", header, " ".repeat(max_len - header.len())));
|
||||||
|
out.push_str(&format!("│{}│\n", "_".repeat(max_len + 2)));
|
||||||
|
out.push_str(&padded_packages.join("\n"));
|
||||||
|
out.push('\n');
|
||||||
|
out.push_str(&format!(" {}", "\u{203e}".repeat(max_len + 2)));
|
||||||
|
for line in license.text.lines() {
|
||||||
|
if line.is_empty() {
|
||||||
|
out.push('\n');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out.push('\n');
|
||||||
|
out.push_str(" ");
|
||||||
|
out.push_str(line);
|
||||||
|
}
|
||||||
|
out.truncate(out.trim_end().len());
|
||||||
|
out.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_filter_dedup_and_sort(sources: Vec<Vec<LicenseEntry>>) -> Vec<LicenseEntry> {
|
||||||
|
let mut all = Vec::new();
|
||||||
|
for source in sources {
|
||||||
|
all.extend(source);
|
||||||
|
}
|
||||||
|
filter(&mut all);
|
||||||
|
let mut all = dedup_by_licence_text(all);
|
||||||
|
all.sort_by(|a, b| b.packages.len().cmp(&a.packages.len()).then(a.text.len().cmp(&b.text.len())));
|
||||||
|
all
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter(licenses: &mut Vec<LicenseEntry>) {
|
||||||
|
licenses.iter_mut().for_each(|l| {
|
||||||
|
l.packages.retain(|p| !(p.authors.len() == 1 && p.authors[0].contains("contact@graphite.art")));
|
||||||
|
});
|
||||||
|
licenses.retain(|l| !l.packages.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dedup_by_licence_text(vec: Vec<LicenseEntry>) -> Vec<LicenseEntry> {
|
||||||
|
let mut map: HashMap<String, LicenseEntry> = HashMap::new();
|
||||||
|
|
||||||
|
for entry in vec {
|
||||||
|
match map.entry(entry.text.clone()) {
|
||||||
|
std::collections::hash_map::Entry::Occupied(mut e) => {
|
||||||
|
e.get_mut().packages.extend(entry.packages);
|
||||||
|
}
|
||||||
|
std::collections::hash_map::Entry::Vacant(e) => {
|
||||||
|
e.insert(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map.into_values().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
fn compress(content: &str) -> Vec<u8> {
|
||||||
|
use std::io::Write;
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let mut writer = lzma_rust2::XzWriter::new(&mut buf, lzma_rust2::XzOptions::default()).unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to create XZ writer: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
writer.write_all(content.as_bytes()).unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to write compressed credits: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
writer.finish().unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to finish XZ compression: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use crate::{LicenceSource, LicenseEntry, Package};
|
||||||
|
|
||||||
|
pub struct NpmLicenseSource {
|
||||||
|
dir: PathBuf,
|
||||||
|
}
|
||||||
|
impl NpmLicenseSource {
|
||||||
|
pub fn new(dir: PathBuf) -> Self {
|
||||||
|
Self { dir }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LicenceSource for NpmLicenseSource {
|
||||||
|
fn licenses(&self) -> Vec<LicenseEntry> {
|
||||||
|
parse(run(&self.dir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::hash::Hash for NpmLicenseSource {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
let lock_path = self.dir.join("package-lock.json");
|
||||||
|
fs::read_to_string(lock_path).unwrap().hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Output = HashMap<String, NpmEntry>;
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct NpmEntry {
|
||||||
|
licenses: Option<String>,
|
||||||
|
repository: Option<String>,
|
||||||
|
#[serde(rename = "licenseFile")]
|
||||||
|
license_file: Option<String>,
|
||||||
|
publisher: Option<String>,
|
||||||
|
email: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(parsed: Output) -> Vec<LicenseEntry> {
|
||||||
|
parsed
|
||||||
|
.iter()
|
||||||
|
.map(|(name, entry)| {
|
||||||
|
let publisher_info = entry.publisher.as_ref().map(|p| {
|
||||||
|
let email_part = entry.email.as_ref().map(|e| format!(" <{}>", e)).unwrap_or_default();
|
||||||
|
format!("{}{}", p, email_part)
|
||||||
|
});
|
||||||
|
|
||||||
|
let pkg = Package {
|
||||||
|
name: name.to_string(),
|
||||||
|
url: entry.repository.clone(),
|
||||||
|
authors: publisher_info.into_iter().collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let license_text = entry.license_file.as_ref().and_then(|p| fs::read_to_string(p).ok()).map(|s| s.to_string()).unwrap_or_default();
|
||||||
|
|
||||||
|
LicenseEntry {
|
||||||
|
name: entry.licenses.clone(),
|
||||||
|
text: license_text,
|
||||||
|
packages: vec![pkg],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(dir: &std::path::Path) -> Output {
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
let mut cmd = Command::new("npx");
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let mut cmd = Command::new("npx.cmd");
|
||||||
|
cmd.args(["license-checker-rseidelsohn", "--production", "--json"]);
|
||||||
|
cmd.current_dir(dir);
|
||||||
|
|
||||||
|
let output = cmd.output().unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to run npx license-checker-rseidelsohn: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
eprintln!("npx license-checker-rseidelsohn failed:\n{}", String::from_utf8_lossy(&output.stderr));
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let json_str = String::from_utf8(output.stdout).expect("Invalid UTF-8 from license-checker");
|
||||||
|
|
||||||
|
serde_json::from_str(&json_str).unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to parse license-checker JSON: {e}");
|
||||||
|
process::exit(1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -19,9 +19,10 @@ Graphite is built with Rust and web technologies, which means you will need to i
|
||||||
Next, install the dependencies required for development builds:
|
Next, install the dependencies required for development builds:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo install cargo-watch
|
|
||||||
cargo install wasm-pack
|
|
||||||
cargo install -f wasm-bindgen-cli@0.2.100
|
cargo install -f wasm-bindgen-cli@0.2.100
|
||||||
|
cargo install wasm-pack
|
||||||
|
cargo install cargo-watch
|
||||||
|
cargo install cargo-about
|
||||||
```
|
```
|
||||||
|
|
||||||
Regarding the last one: you'll likely get faster build times if you manually install that specific version of `wasm-bindgen-cli`. It is supposed to be installed automatically but a version mismatch causes it to reinstall every single recompilation. It may need to be manually updated periodically to match the version of the `wasm-bindgen` dependency in [`Cargo.toml`](https://github.com/GraphiteEditor/Graphite/blob/master/Cargo.toml).
|
Regarding the last one: you'll likely get faster build times if you manually install that specific version of `wasm-bindgen-cli`. It is supposed to be installed automatically but a version mismatch causes it to reinstall every single recompilation. It may need to be manually updated periodically to match the version of the `wasm-bindgen` dependency in [`Cargo.toml`](https://github.com/GraphiteEditor/Graphite/blob/master/Cargo.toml).
|
||||||
|
|
@ -66,13 +67,7 @@ npm run production
|
||||||
<details>
|
<details>
|
||||||
<summary>Production build instructions: click here</summary>
|
<summary>Production build instructions: click here</summary>
|
||||||
|
|
||||||
You'll rarely need to compile your own production builds because our CI/CD system takes care of deployments. However, you can compile a production build with full optimizations by first installing the additional `cargo-about` dev dependency:
|
You'll rarely need to compile your own production builds because our CI/CD system takes care of deployments. However, you can compile a production build with full optimizations by running:
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo install cargo-about
|
|
||||||
```
|
|
||||||
|
|
||||||
And then running:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm run build
|
npm run build
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue