From 782c8b44ddda7b5c51ad22d388d93bb39f05586e Mon Sep 17 00:00:00 2001 From: Gabriel Fontes Date: Thu, 4 May 2023 20:20:03 -0300 Subject: [PATCH 1/4] buildDartApplication: init This adds a function for easily packaging non-flutter dart apps. --- doc/languages-frameworks/dart.section.md | 36 ++++++++++ doc/languages-frameworks/index.xml | 1 + .../dart/build-dart-application/default.nix | 66 +++++++++++++++++++ .../hooks/dart-build-hook.sh | 34 ++++++++++ .../hooks/dart-config-hook.sh | 12 ++++ .../hooks/dart-install-hook.sh | 29 ++++++++ .../build-dart-application/hooks/default.nix | 15 +++++ pkgs/top-level/all-packages.nix | 6 ++ 8 files changed, 199 insertions(+) create mode 100644 doc/languages-frameworks/dart.section.md create mode 100644 pkgs/build-support/dart/build-dart-application/default.nix create mode 100644 pkgs/build-support/dart/build-dart-application/hooks/dart-build-hook.sh create mode 100644 pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh create mode 100644 pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh create mode 100644 pkgs/build-support/dart/build-dart-application/hooks/default.nix diff --git a/doc/languages-frameworks/dart.section.md b/doc/languages-frameworks/dart.section.md new file mode 100644 index 000000000000..eb7c82748ad6 --- /dev/null +++ b/doc/languages-frameworks/dart.section.md @@ -0,0 +1,36 @@ +# Dart {#sec-language-dart} + +## Dart applications {#ssec-dart-applications} + +The function `buildDartApplication` builds Dart applications managed with pub. + +It fetches its Dart dependencies automatically through `fetchDartDeps`, and (through a series of hooks) builds and installs the executables specified in the pubspec file. The hooks can be used in other derivations, if needed. The phases can also be overridden to do something different from installing binaries. + +If you are packaging a Flutter desktop application, use the `buildFlutterApplication` function instead. + +`vendorHash`: is the hash of the output of the dependency fetcher derivation. To obtain it, simply set it to `lib.fakeHash` (or omit it) and run the build ([more details here](#sec-source-hashes)). + +If the upstream source is missing a `pubspec.lock` file, you'll have to vendor one and specify it using `pubspecLockFile`. If it is needed, one will be generated for you and printed when attempting to build the derivation. + +The `dart` commands run can be overridden through `pubGetScript` and `dartCompileCommand`, you can also add flags using `dartCompileFlags` or `dartJitFlags`. + +Dart supports multiple [outputs types](https://dart.dev/tools/dart-compile#types-of-output), you can choose between them using `dartOutputType` (defaults to `exe`). If you want to override the binaries path or the source path they come from, you can use `dartEntryPoints`. Outputs that require a runtime will automatically be wrapped with the relevant runtime (`dartaotruntime` for `aot-snapshot`, `dart run` for `jit-snapshot` and `kernel`, `node` for `js`), this can be overridden through `dartRuntimeCommand`. + +```nix +{ buildDartApplication, fetchFromGitHub }: + +buildDartApplication rec { + pname = "dart-sass"; + version = "1.62.1"; + + src = fetchFromGitHub { + owner = "sass"; + repo = pname; + rev = version; + hash = "sha256-U6enz8yJcc4Wf8m54eYIAnVg/jsGi247Wy8lp1r1wg4="; + }; + + pubspecLockFile = ./pubspec.lock; + vendorHash = "sha256-Atm7zfnDambN/BmmUf4BG0yUz/y6xWzf0reDw3Ad41s="; +} +``` diff --git a/doc/languages-frameworks/index.xml b/doc/languages-frameworks/index.xml index e1e35564e321..94c4e303027f 100644 --- a/doc/languages-frameworks/index.xml +++ b/doc/languages-frameworks/index.xml @@ -14,6 +14,7 @@ + diff --git a/pkgs/build-support/dart/build-dart-application/default.nix b/pkgs/build-support/dart/build-dart-application/default.nix new file mode 100644 index 000000000000..780aefa90ef9 --- /dev/null +++ b/pkgs/build-support/dart/build-dart-application/default.nix @@ -0,0 +1,66 @@ +{ lib, stdenv, fetchDartDeps, writeText, dartHooks, makeWrapper, dart, nodejs }: + +{ pubGetScript ? "dart pub get" + + # Output type to produce. Can be any kind supported by dart + # https://dart.dev/tools/dart-compile#types-of-output + # If using jit, you might want to pass some arguments to `dartJitFlags` +, dartOutputType ? "exe" +, dartCompileCommand ? "dart compile" +, dartCompileFlags ? [ ] + # These come at the end of the command, useful to pass flags to the jit run +, dartJitFlags ? [ ] + + # Attrset of entry point files to build and install. + # Where key is the final binary path and value is the source file path + # e.g. { "bin/foo" = "bin/main.dart"; } + # Set to null to read executables from pubspec.yaml +, dartEntryPoints ? null + # Used when wrapping aot, jit, kernel, and js builds. + # Set to null to disable wrapping. +, dartRuntimeCommand ? + if dartOutputType == "aot-snapshot" then "${dart}/bin/dartaotruntime" + else if (dartOutputType == "jit-snapshot" || dartOutputType == "kernel") then "${dart}/bin/dart" + else if dartOutputType == "js" then "${nodejs}/bin/node" + else null + +, pubspecLockFile ? null +, vendorHash ? "" +, ... +}@args: + +let + dartDeps = fetchDartDeps { + buildDrvArgs = args; + inherit pubGetScript vendorHash pubspecLockFile; + }; + inherit (dartHooks.override { inherit dart; }) dartConfigHook dartBuildHook dartInstallHook; +in +assert !(builtins.isString dartOutputType && dartOutputType != "") -> + throw "dartOutputType must be a non-empty string"; +stdenv.mkDerivation (args // { + inherit pubGetScript dartCompileCommand dartOutputType dartRuntimeCommand + dartCompileFlags dartJitFlags; + + dartEntryPoints = + if (dartEntryPoints != null) + then writeText "entrypoints.json" (builtins.toJSON dartEntryPoints) + else null; + + nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [ + dart + dartDeps + dartConfigHook + dartBuildHook + dartInstallHook + makeWrapper + ]; + + # When stripping, it seems some ELF information is lost and the dart VM cli + # runs instead of the expected program. Don't strip if it's an exe output. + dontStrip = args.dontStrip or (dartOutputType == "exe"); + + passthru = { inherit dartDeps; } // (args.passthru or { }); + + meta = (args.meta or { }) // { platforms = args.meta.platforms or dart.meta.platforms; }; +}) diff --git a/pkgs/build-support/dart/build-dart-application/hooks/dart-build-hook.sh b/pkgs/build-support/dart/build-dart-application/hooks/dart-build-hook.sh new file mode 100644 index 000000000000..23ebfbd6e66e --- /dev/null +++ b/pkgs/build-support/dart/build-dart-application/hooks/dart-build-hook.sh @@ -0,0 +1,34 @@ +# shellcheck shell=bash + +# Outputs line-separated "${dest}\t${source}" +_getDartEntryPoints() { + if [ -n "$dartEntryPoints" ]; then + @jq@ -r '(to_entries | map(.key + "\t" + .value) | join("\n"))' "$dartEntryPoints" + else + # The pubspec executables section follows the pattern: + # : [source-file-name] + # Where source-file-name defaults to output-bin-name if omited + @yq@ -r '(.executables | to_entries | map("bin/" + .key + "\t" + "bin/" + (.value // .key) + ".dart") | join("\n"))' pubspec.yaml + fi +} + +dartBuildHook() { + echo "Executing dartBuildHook" + + runHook preBuild + + while IFS=$'\t' read -ra target; do + dest="${target[0]}" + src="${target[1]}" + eval "$dartCompileCommand" "$dartOutputType" \ + -o "$dest" "${dartCompileFlags[@]}" "$src" "${dartJitFlags[@]}" + done < <(_getDartEntryPoints) + + runHook postBuild + + echo "Finished dartBuildHook" +} + +if [ -z "${dontDartBuild-}" ] && [ -z "${buildPhase-}" ]; then + buildPhase=dartBuildHook +fi diff --git a/pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh b/pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh new file mode 100644 index 000000000000..ee610f673d2b --- /dev/null +++ b/pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh @@ -0,0 +1,12 @@ +# shellcheck shell=bash + +dartConfigHook() { + echo "Executing dartConfigHook" + + echo "Installing dependencies" + eval "$pubGetScript" --offline + + echo "Finished dartConfigHook" +} + +postConfigureHooks+=(dartConfigHook) diff --git a/pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh b/pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh new file mode 100644 index 000000000000..1906bcfbca4c --- /dev/null +++ b/pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh @@ -0,0 +1,29 @@ +# shellcheck shell=bash + +dartInstallHook() { + echo "Executing dartInstallHook" + + runHook preInstall + + mkdir -p "$out" + + while IFS=$'\t' read -ra target; do + dest="${target[0]}" + # Wrap with runtime command, if it's defined + if [ -n "$dartRuntimeCommand" ]; then + install -D "$dest" "$out/share/$dest" + makeWrapper "$dartRuntimeCommand" "$out/$dest" \ + --add-flags "$out/share/$dest" + else + install -Dm755 "$dest" "$out/$dest" + fi + done < <(_getDartEntryPoints) + + runHook postInstall + + echo "Finished dartInstallHook" +} + +if [ -z "${dontDartInstall-}" ] && [ -z "${installPhase-}" ]; then + installPhase=dartInstallHook +fi diff --git a/pkgs/build-support/dart/build-dart-application/hooks/default.nix b/pkgs/build-support/dart/build-dart-application/hooks/default.nix new file mode 100644 index 000000000000..463061c54a8d --- /dev/null +++ b/pkgs/build-support/dart/build-dart-application/hooks/default.nix @@ -0,0 +1,15 @@ +{ lib, makeSetupHook, dart, yq, jq }: + +{ + dartConfigHook = makeSetupHook { + name = "dart-config-hook"; + } ./dart-config-hook.sh; + dartBuildHook = makeSetupHook { + name = "dart-build-hook"; + substitutions.yq = "${yq}/bin/yq"; + substitutions.jq = "${jq}/bin/jq"; + } ./dart-build-hook.sh; + dartInstallHook = makeSetupHook { + name = "dart-install-hook"; + } ./dart-install-hook.sh; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 34ab73e97b24..770a1b8574e9 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -39795,6 +39795,12 @@ with pkgs; dart-sass = callPackage ../development/tools/misc/dart-sass { }; + fetchDartDeps = callPackage ../build-support/dart/fetch-dart-deps { }; + + buildDartApplication = callPackage ../build-support/dart/build-dart-application { }; + + dartHooks = callPackage ../build-support/dart/build-dart-application/hooks { }; + httrack = callPackage ../tools/backup/httrack { }; httraqt = libsForQt5.callPackage ../tools/backup/httrack/qt.nix { }; From d161ef5631ae8826255e6c517b4fa59c369bf0f2 Mon Sep 17 00:00:00 2001 From: Gabriel Fontes Date: Thu, 4 May 2023 20:22:04 -0300 Subject: [PATCH 2/4] buildFlutterApplication: add docs --- doc/languages-frameworks/dart.section.md | 31 +++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/doc/languages-frameworks/dart.section.md b/doc/languages-frameworks/dart.section.md index eb7c82748ad6..b00327b78eb2 100644 --- a/doc/languages-frameworks/dart.section.md +++ b/doc/languages-frameworks/dart.section.md @@ -6,7 +6,7 @@ The function `buildDartApplication` builds Dart applications managed with pub. It fetches its Dart dependencies automatically through `fetchDartDeps`, and (through a series of hooks) builds and installs the executables specified in the pubspec file. The hooks can be used in other derivations, if needed. The phases can also be overridden to do something different from installing binaries. -If you are packaging a Flutter desktop application, use the `buildFlutterApplication` function instead. +If you are packaging a Flutter desktop application, use [`buildFlutterApplication`](#ssec-dart-flutter) instead. `vendorHash`: is the hash of the output of the dependency fetcher derivation. To obtain it, simply set it to `lib.fakeHash` (or omit it) and run the build ([more details here](#sec-source-hashes)). @@ -34,3 +34,32 @@ buildDartApplication rec { vendorHash = "sha256-Atm7zfnDambN/BmmUf4BG0yUz/y6xWzf0reDw3Ad41s="; } ``` + +## Flutter applications {#ssec-dart-flutter} + +The function `buildFlutterApplication` builds Flutter applications. + +The deps.json file must always be provided when packaging in Nixpkgs. It will be generated and printed if the derivation is attempted to be built without one. Alternatively, `autoDepsList` may be set to `true` when outside of Nixpkgs, as it relies on import-from-derivation. + +A `pubspec.lock` file must be available. See the [Dart documentation](#ssec-dart-applications) for more details. + +```nix +{ flutter, fetchFromGitHub }: + +flutter.buildFlutterApplication { + pname = "firmware-updater"; + version = "unstable-2023-04-30"; + + src = fetchFromGitHub { + owner = "canonical"; + repo = "firmware-updater"; + rev = "6e7dbdb64e344633ea62874b54ff3990bd3b8440"; + sha256 = "sha256-s5mwtr5MSPqLMN+k851+pFIFFPa0N1hqz97ys050tFA="; + fetchSubmodules = true; + }; + + pubspecLockFile = ./pubspec.lock; + depsListFile = ./deps.json; + vendorHash = "sha256-cdMO+tr6kYiN5xKXa+uTMAcFf2C75F3wVPrn21G4QPQ="; +} +``` From 211d7f629255cfa14ed63b363271afd35ab53d63 Mon Sep 17 00:00:00 2001 From: Gabriel Fontes Date: Thu, 4 May 2023 20:22:22 -0300 Subject: [PATCH 3/4] dart-sass: use buildDartApplication --- .../tools/misc/dart-sass/default.nix | 39 ++++--------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/pkgs/development/tools/misc/dart-sass/default.nix b/pkgs/development/tools/misc/dart-sass/default.nix index 1035d29d4ce7..b8cc7265fb08 100644 --- a/pkgs/development/tools/misc/dart-sass/default.nix +++ b/pkgs/development/tools/misc/dart-sass/default.nix @@ -1,54 +1,29 @@ { lib -, stdenvNoCC , fetchFromGitHub -, dart -, callPackage +, buildDartApplication }: -stdenvNoCC.mkDerivation (finalAttrs: rec { +buildDartApplication rec { pname = "dart-sass"; version = "1.62.1"; src = fetchFromGitHub { owner = "sass"; repo = pname; - rev = finalAttrs.version; + rev = version; hash = "sha256-U6enz8yJcc4Wf8m54eYIAnVg/jsGi247Wy8lp1r1wg4="; }; - nativeBuildInputs = [ - dart - (callPackage ../../../../build-support/dart/fetch-dart-deps { } { - buildDrvArgs = finalAttrs; - pubspecLockFile = ./pubspec.lock; - vendorHash = "sha256-Atm7zfnDambN/BmmUf4BG0yUz/y6xWzf0reDw3Ad41s="; - }) - ]; + pubspecLockFile = ./pubspec.lock; + vendorHash = "sha256-Atm7zfnDambN/BmmUf4BG0yUz/y6xWzf0reDw3Ad41s="; - configurePhase = '' - runHook preConfigure - dart pub get --offline - runHook postConfigure - ''; - - buildPhase = '' - runHook preBuild - dart compile exe --define=version=${finalAttrs.version} ./bin/sass.dart - runHook postBuild - ''; - - installPhase = '' - runHook preInstall - install -D ./bin/sass.exe $out/bin/sass - runHook postInstall - ''; + dartCompileFlags = "--define=version=${version}"; meta = with lib; { - inherit (dart.meta) platforms; homepage = "https://github.com/sass/dart-sass"; description = "The reference implementation of Sass, written in Dart"; mainProgram = "sass"; license = licenses.mit; maintainers = with maintainers; [ lelgenio ]; }; -}) +} From 70d3f5c0b4814d63ff364eec75b3b82d3e09a39c Mon Sep 17 00:00:00 2001 From: Gabriel Fontes Date: Thu, 4 May 2023 20:26:13 -0300 Subject: [PATCH 4/4] buildFlutterApplication: default vendorHash to "" This makes it more ergonomical to use, and is consistent with buildDartApplication. --- pkgs/build-support/flutter/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/build-support/flutter/default.nix b/pkgs/build-support/flutter/default.nix index abb1c8ac359f..79d23d7535a2 100644 --- a/pkgs/build-support/flutter/default.nix +++ b/pkgs/build-support/flutter/default.nix @@ -16,7 +16,7 @@ , customPackageOverrides ? { } , autoDepsList ? false , depsListFile ? null -, vendorHash +, vendorHash ? "" , pubspecLockFile ? null , nativeBuildInputs ? [ ] , preUnpack ? ""