diff options
Diffstat (limited to 'nixpkgs/pkgs/build-support/dart/build-dart-application')
7 files changed, 408 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/build-support/dart/build-dart-application/default.nix b/nixpkgs/pkgs/build-support/dart/build-dart-application/default.nix new file mode 100644 index 000000000000..c99b8bbf325e --- /dev/null +++ b/nixpkgs/pkgs/build-support/dart/build-dart-application/default.nix @@ -0,0 +1,132 @@ +{ lib +, stdenv +, callPackage +, runCommand +, writeText +, pub2nix +, dartHooks +, makeWrapper +, dart +, nodejs +, darwin +, jq +, yq +}: + +{ src +, sourceRoot ? "source" +, packageRoot ? (lib.removePrefix "/" (lib.removePrefix "source" sourceRoot)) +, gitHashes ? { } +, sdkSourceBuilders ? { } +, customSourceBuilders ? { } + +, sdkSetupScript ? "" +, extraPackageConfigSetup ? "" + + # 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 + +, runtimeDependencies ? [ ] +, extraWrapProgramArgs ? "" + +, autoPubspecLock ? null +, pubspecLock ? if autoPubspecLock == null then + throw "The pubspecLock argument is required. If import-from-derivation is allowed (it isn't in Nixpkgs), you can set autoPubspecLock to the path to a pubspec.lock instead." + else + assert lib.assertMsg (builtins.pathExists autoPubspecLock) "The pubspec.lock file could not be found!"; + lib.importJSON (runCommand "${lib.getName args}-pubspec-lock-json" { nativeBuildInputs = [ yq ]; } ''yq . '${autoPubspecLock}' > "$out"'') +, ... +}@args: + +let + generators = callPackage ./generators.nix { inherit dart; } { buildDrvArgs = args; }; + + pubspecLockFile = builtins.toJSON pubspecLock; + pubspecLockData = pub2nix.readPubspecLock { inherit src packageRoot pubspecLock gitHashes sdkSourceBuilders customSourceBuilders; }; + packageConfig = generators.linkPackageConfig { + packageConfig = pub2nix.generatePackageConfig { + pname = if args.pname != null then "${args.pname}-${args.version}" else null; + + dependencies = + # Ideally, we'd only include the main dependencies and their transitive + # dependencies. + # + # The pubspec.lock file does not contain information about where + # transitive dependencies come from, though, and it would be weird to + # include the transitive dependencies of dev and override dependencies + # without including the dev and override dependencies themselves. + builtins.concatLists (builtins.attrValues pubspecLockData.dependencies); + + inherit (pubspecLockData) dependencySources; + }; + extraSetupCommands = extraPackageConfigSetup; + }; + + inherit (dartHooks.override { inherit dart; }) dartConfigHook dartBuildHook dartInstallHook dartFixupHook; + + baseDerivation = stdenv.mkDerivation (finalAttrs: (builtins.removeAttrs args [ "gitHashes" "sdkSourceBuilders" "pubspecLock" "customSourceBuilders" ]) // { + inherit pubspecLockFile packageConfig sdkSetupScript + dartCompileCommand dartOutputType dartRuntimeCommand dartCompileFlags + dartJitFlags; + + outputs = [ "out" "pubcache" ] ++ args.outputs or [ ]; + + dartEntryPoints = + if (dartEntryPoints != null) + then writeText "entrypoints.json" (builtins.toJSON dartEntryPoints) + else null; + + runtimeDependencies = map lib.getLib runtimeDependencies; + + nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [ + dart + dartConfigHook + dartBuildHook + dartInstallHook + dartFixupHook + makeWrapper + jq + ] ++ lib.optionals stdenv.isDarwin [ + darwin.sigtool + ] ++ + # Ensure that we inherit the propagated build inputs from the dependencies. + builtins.attrValues pubspecLockData.dependencySources; + + preConfigure = args.preConfigure or "" + '' + ln -sf "$pubspecLockFilePath" pubspec.lock + ''; + + # 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"); + + passAsFile = [ "pubspecLockFile" ]; + + passthru = { + pubspecLock = pubspecLockData; + } // (args.passthru or { }); + + meta = (args.meta or { }) // { platforms = args.meta.platforms or dart.meta.platforms; }; + }); +in +assert !(builtins.isString dartOutputType && dartOutputType != "") -> +throw "dartOutputType must be a non-empty string"; +baseDerivation diff --git a/nixpkgs/pkgs/build-support/dart/build-dart-application/generators.nix b/nixpkgs/pkgs/build-support/dart/build-dart-application/generators.nix new file mode 100644 index 000000000000..f01a09305dba --- /dev/null +++ b/nixpkgs/pkgs/build-support/dart/build-dart-application/generators.nix @@ -0,0 +1,74 @@ +{ lib +, stdenvNoCC +, dart +, dartHooks +, jq +, yq +, cacert +}: + +{ + # Arguments used in the derivation that builds the Dart package. + # Passing these is recommended to ensure that the same steps are made to + # prepare the sources in both this derivation and the one that builds the Dart + # package. + buildDrvArgs ? { } +, ... +}@args: + +# This is a derivation and setup hook that can be used to fetch dependencies for Dart projects. +# It is designed to be placed in the nativeBuildInputs of a derivation that builds a Dart package. +# Providing the buildDrvArgs argument is highly recommended. +let + buildDrvInheritArgNames = [ + "name" + "pname" + "version" + "src" + "sourceRoot" + "setSourceRoot" + "preUnpack" + "unpackPhase" + "unpackCmd" + "postUnpack" + "prePatch" + "patchPhase" + "patches" + "patchFlags" + "postPatch" + ]; + + buildDrvInheritArgs = builtins.foldl' + (attrs: arg: + if buildDrvArgs ? ${arg} + then attrs // { ${arg} = buildDrvArgs.${arg}; } + else attrs) + { } + buildDrvInheritArgNames; + + drvArgs = buildDrvInheritArgs // (removeAttrs args [ "buildDrvArgs" ]); + name = (if drvArgs ? name then drvArgs.name else "${drvArgs.pname}-${drvArgs.version}"); + + # Adds the root package to a dependency package_config.json file from pub2nix. + linkPackageConfig = { packageConfig, extraSetupCommands ? "" }: stdenvNoCC.mkDerivation (drvArgs // { + name = "${name}-package-config-with-root.json"; + + nativeBuildInputs = drvArgs.nativeBuildInputs or [ ] ++ args.nativeBuildInputs or [ ] ++ [ jq yq ]; + + dontBuild = true; + + installPhase = '' + runHook preInstall + + packageName="$(yq --raw-output .name pubspec.yaml)" + jq --arg name "$packageName" '.packages |= . + [{ name: $name, rootUri: "../", packageUri: "lib/" }]' '${packageConfig}' > "$out" + ${extraSetupCommands} + + runHook postInstall + ''; + }); +in +{ + inherit + linkPackageConfig; +} diff --git a/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/dart-build-hook.sh b/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/dart-build-hook.sh new file mode 100644 index 000000000000..23ebfbd6e66e --- /dev/null +++ b/nixpkgs/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: + # <output-bin-name>: [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/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh b/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh new file mode 100644 index 000000000000..50754a7b56d4 --- /dev/null +++ b/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh @@ -0,0 +1,70 @@ +# shellcheck shell=bash + +dartConfigHook() { + echo "Executing dartConfigHook" + + echo "Setting up SDK" + eval "$sdkSetupScript" + + echo "Installing dependencies" + mkdir -p .dart_tool + cp "$packageConfig" .dart_tool/package_config.json + + packagePath() { + jq --raw-output --arg name "$1" '.packages.[] | select(.name == $name) .rootUri | sub("file://"; "")' .dart_tool/package_config.json + } + + # Runs a Dart executable from a package with a custom path. + # + # Usage: + # packageRunCustom <package> [executable] [bin_dir] + # + # By default, [bin_dir] is "bin", and [executable] is <package>. + # i.e. `packageRunCustom build_runner` is equivalent to `packageRunCustom build_runner build_runner bin`, which runs `bin/build_runner.dart` from the build_runner package. + packageRunCustom() { + local args=() + local passthrough=() + + while [ $# -gt 0 ]; do + if [ "$1" != "--" ]; then + args+=("$1") + shift + else + shift + passthrough=("$@") + break + fi + done + + local name="${args[0]}" + local path="${args[1]:-$name}" + local prefix="${args[2]:-bin}" + + dart --packages=.dart_tool/package_config.json "$(packagePath "$name")/$prefix/$path.dart" "${passthrough[@]}" + } + + # Runs a Dart executable from a package. + # + # Usage: + # packageRun <package> [-e executable] [...] + # + # To run an executable from an unconventional location, use packageRunCustom. + packageRun() { + local name="$1" + shift + + local executableName="$name" + if [ "$1" = "-e" ]; then + shift + executableName="$1" + shift + fi + + fileName="$(@yq@ --raw-output --arg name "$executableName" '.executables.[$name] // $name' "$(packagePath "$name")/pubspec.yaml")" + packageRunCustom "$name" "$fileName" -- "$@" + } + + echo "Finished dartConfigHook" +} + +postConfigureHooks+=(dartConfigHook) diff --git a/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/dart-fixup-hook.sh b/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/dart-fixup-hook.sh new file mode 100644 index 000000000000..60bd74871c92 --- /dev/null +++ b/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/dart-fixup-hook.sh @@ -0,0 +1,35 @@ +# shellcheck shell=bash + +dartFixupHook() { + echo "Executing dartFixupHook" + + declare -a wrapProgramArgs + + # Add runtime library dependencies to the LD_LIBRARY_PATH. + # For some reason, the RUNPATH of the executable is not used to load dynamic libraries in dart:ffi with DynamicLibrary.open(). + # + # This could alternatively be fixed with patchelf --add-needed, but this would cause all the libraries to be opened immediately, + # which is not what application authors expect. + APPLICATION_LD_LIBRARY_PATH="" + for runtimeDependency in "${runtimeDependencies[@]}"; do + addToSearchPath APPLICATION_LD_LIBRARY_PATH "${runtimeDependency}/lib" + done + if [[ ! -z "$APPLICATION_LD_LIBRARY_PATH" ]]; then + wrapProgramArgs+=(--suffix LD_LIBRARY_PATH : \"$APPLICATION_LD_LIBRARY_PATH\") + fi + + if [[ ! -z "$extraWrapProgramArgs" ]]; then + wrapProgramArgs+=("$extraWrapProgramArgs") + fi + + if [ ${#wrapProgramArgs[@]} -ne 0 ]; then + for f in "$out"/bin/*; do + echo "Wrapping $f..." + eval "wrapProgram \"$f\" ${wrapProgramArgs[@]}" + done + fi + + echo "Finished dartFixupHook" +} + +postFixupHooks+=(dartFixupHook) diff --git a/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh b/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh new file mode 100644 index 000000000000..349a0dfdef0e --- /dev/null +++ b/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh @@ -0,0 +1,43 @@ +# shellcheck shell=bash + +dartInstallHook() { + echo "Executing dartInstallHook" + + runHook preInstall + + # Install snapshots and executables. + 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" +} + +dartInstallCacheHook() { + echo "Executing dartInstallCacheHook" + + # Install the package_config.json file. + mkdir -p "$pubcache" + cp .dart_tool/package_config.json "$pubcache/package_config.json" + + echo "Finished dartInstallCacheHook" +} + +if [ -z "${dontDartInstall-}" ] && [ -z "${installPhase-}" ]; then + installPhase=dartInstallHook +fi + +if [ -z "${dontDartInstallCache-}" ]; then + postInstallHooks+=(dartInstallCacheHook) +fi diff --git a/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/default.nix b/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/default.nix new file mode 100644 index 000000000000..253d3132ad02 --- /dev/null +++ b/nixpkgs/pkgs/build-support/dart/build-dart-application/hooks/default.nix @@ -0,0 +1,20 @@ +{ lib, makeSetupHook, dart, yq, jq }: + +{ + dartConfigHook = makeSetupHook { + name = "dart-config-hook"; + substitutions.yq = "${yq}/bin/yq"; + substitutions.jq = "${jq}/bin/jq"; + } ./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; + dartFixupHook = makeSetupHook { + name = "dart-fixup-hook"; + } ./dart-fixup-hook.sh; +} |