flutter.buildFlutterApplication: add support for finalAttrs (#494789)

This commit is contained in:
Tristan Ross 2026-03-04 22:01:34 +00:00 committed by GitHub
commit f6442fb72a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 203 additions and 179 deletions

View file

@ -36,19 +36,19 @@ Dart supports multiple [outputs types](https://dart.dev/tools/dart-compile#types
fetchFromGitHub,
}:
buildDartApplication rec {
buildDartApplication (finalAttrs: {
pname = "dart-sass";
version = "1.62.1";
src = fetchFromGitHub {
owner = "sass";
repo = "dart-sass";
tag = version;
tag = finalAttrs.version;
hash = "sha256-U6enz8yJcc4Wf8m54eYIAnVg/jsGi247Wy8lp1r1wg4=";
};
pubspecLock = lib.importJSON ./pubspec.lock.json;
}
})
```
### Patching dependencies {#ssec-dart-applications-patching-dependencies}
@ -102,14 +102,14 @@ The function `buildFlutterApplication` builds Flutter applications.
See the [Dart documentation](#ssec-dart-applications) for more details on required files and arguments.
`flutter` in Nixpkgs always points to `flutterPackages.stable`, which is the latest packaged version. To avoid unforeseen breakage during upgrade, packages in Nixpkgs should use a specific flutter version, such as `flutter319` and `flutter322`, instead of using `flutter` directly.
`flutter` in Nixpkgs always points to `flutterPackages.stable`, which is the latest packaged version. To avoid unforeseen breakage during upgrade, packages in Nixpkgs should use a specific flutter version, such as `flutter335` and `flutter338`, instead of using `flutter` directly.
```nix
{ flutter322, fetchFromGitHub }:
{ flutter335, fetchFromGitHub }:
flutter322.buildFlutterApplication {
flutter335.buildFlutterApplication (finalAttrs: {
pname = "firmware-updater";
version = "0-unstable-2023-04-30";
version = "0-unstable-2025-09-09";
# To build for the Web, use the targetFlutterPlatform argument.
# targetFlutterPlatform = "web";
@ -117,13 +117,17 @@ flutter322.buildFlutterApplication {
src = fetchFromGitHub {
owner = "canonical";
repo = "firmware-updater";
rev = "6e7dbdb64e344633ea62874b54ff3990bd3b8440";
hash = "sha256-s5mwtr5MSPqLMN+k851+pFIFFPa0N1hqz97ys050tFA=";
rev = "402e97254b9d63c8d962c46724995e377ff922c8";
hash = "sha256-nQn5mlgNj157h++67+mhez/F1ALz4yY+bxiGsi0/xX8=";
fetchSubmodules = true;
};
pubspecLock = lib.importJSON ./pubspec.lock.json;
}
sourceRoot = "${finalAttrs.src.name}/apps/firmware_updater";
gitHashes.fwupd = "sha256-l/+HrrJk1mE2Mrau+NmoQ7bu9qhHU6wX68+m++9Hjd4=";
})
```
### Usage with nix-shell {#ssec-dart-flutter-nix-shell}

View file

@ -14,123 +14,137 @@
yq,
}:
{
src,
sourceRoot ? "source",
packageRoot ? (lib.removePrefix "/" (lib.removePrefix "source" sourceRoot)),
gitHashes ? { },
sdkSourceBuilders ? { },
customSourceBuilders ? { },
lib.extendMkDerivation {
constructDrv = stdenv.mkDerivation;
sdkSetupScript ? "",
extraPackageConfigSetup ? "",
excludeDrvArgNames = [
"gitHashes"
"sdkSourceBuilders"
"pubspecLock"
"customSourceBuilders"
];
# 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
customSourceBuilders
;
sdkSourceBuilders = {
# https://github.com/dart-lang/pub/blob/e1fbda73d1ac597474b82882ee0bf6ecea5df108/lib/src/sdk/dart.dart#L80
"dart" =
name:
runCommand "dart-sdk-${name}" { passthru.packageRoot = "."; } ''
for path in '${dart}/pkg/${name}'; do
if [ -d "$path" ]; then
ln -s "$path" "$out"
break
fi
done
if [ ! -e "$out" ]; then
echo 1>&2 'The Dart SDK does not contain the requested package: ${name}!'
exit 1
fi
'';
}
// sdkSourceBuilders;
};
packageConfig = generators.linkPackageConfig {
inherit pubspecLock;
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 (
extendDrvArgs =
finalAttrs:
(removeAttrs args [
args@{
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"''
),
...
}:
let
generators = callPackage ./generators.nix { inherit dart; } { buildDrvArgs = args; };
pubspecLockFile = builtins.toJSON pubspecLock;
pubspecLockData = pub2nix.readPubspecLock {
inherit
src
packageRoot
pubspecLock
gitHashes
customSourceBuilders
;
sdkSourceBuilders = {
# https://github.com/dart-lang/pub/blob/e1fbda73d1ac597474b82882ee0bf6ecea5df108/lib/src/sdk/dart.dart#L80
"dart" =
name:
runCommand "dart-sdk-${name}" { passthru.packageRoot = "."; } ''
for path in '${dart}/pkg/${name}'; do
if [ -d "$path" ]; then
ln -s "$path" "$out"
break
fi
done
if[ ! -e "$out" ]; then
echo 1>&2 'The Dart SDK does not contain the requested package: ${name}!'
exit 1
fi
'';
}
// sdkSourceBuilders;
};
packageConfig = generators.linkPackageConfig {
inherit pubspecLock;
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
;
in
assert
!(builtins.isString dartOutputType && dartOutputType != "")
-> throw "dartOutputType must be a non-empty string";
(builtins.removeAttrs args [
"gitHashes"
"sdkSourceBuilders"
"pubspecLock"
@ -198,10 +212,5 @@ let
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
};
}

View file

@ -22,14 +22,14 @@ let
media_kit_hash = "sha256-oJQ9sRQI4HpAIzoS995yfnzvx5ZzIubVANzbmxTt6LE=";
in
flutter.buildFlutterApplication rec {
flutter.buildFlutterApplication (finalAttrs: {
pname = "fladder";
version = "0.10.1";
src = fetchFromGitHub {
owner = "DonutWare";
repo = "Fladder";
tag = "v${version}";
tag = "v${finalAttrs.version}";
hash = "sha256-lmtEgBxCmEYcckhSAXhMPDzNQBluTyW0yjkt6Rr9byA=";
};
@ -107,4 +107,4 @@ flutter.buildFlutterApplication rec {
];
mainProgram = "Fladder";
};
}
})

View file

@ -14,33 +14,63 @@
# absolutely no mac support for now
{
pubGetScript ? null,
flutterBuildFlags ? [ ],
targetFlutterPlatform ? "linux",
extraWrapProgramArgs ? "",
flutterMode ? null,
...
}@args:
let
hasEngine = flutter ? engine && flutter.engine != null && flutter.engine.meta.available;
flutterMode = args.flutterMode or (if hasEngine then flutter.engine.runtimeMode else "release");
flutterFlags = lib.optional hasEngine "--local-engine host_${flutterMode}${
lib.optionalString (!flutter.engine.isOptimized) "_unopt"
}";
flutterBuildFlags = [
"--${flutterMode}"
]
++ (args.flutterBuildFlags or [ ])
++ flutterFlags;
builderArgs =
lib.extendMkDerivation {
constructDrv =
argsFn:
let
evalArgs = lib.fix argsFn;
targetFlutterPlatform = evalArgs.targetFlutterPlatform or "linux";
minimalFlutter = flutter.override {
supportedTargetFlutterPlatforms = [
"universal"
targetFlutterPlatform
];
};
buildAppWith = flutter: buildDartApplication.override { dart = flutter; };
in
buildAppWith minimalFlutter (
finalAttrs:
let
args = argsFn finalAttrs;
in
args
// {
passthru = (args.passthru or { }) // {
multiShell = buildAppWith flutter args;
};
}
);
extendDrvArgs =
finalAttrs:
args@{
pubGetScript ? null,
flutterBuildFlags ? [ ],
targetFlutterPlatform ? "linux",
extraWrapProgramArgs ? "",
flutterMode ? null,
...
}:
let
hasEngine = flutter ? engine && flutter.engine != null && flutter.engine.meta.available;
flutterMode' = args.flutterMode or (if hasEngine then flutter.engine.runtimeMode else "release");
flutterFlags = lib.optional hasEngine "--local-engine host_${flutterMode'}${
lib.optionalString (!flutter.engine.isOptimized) "_unopt"
}";
flutterBuildFlags' = [
"--${flutterMode'}"
]
++ (args.flutterBuildFlags or [ ])
++ flutterFlags;
universal = args // {
inherit flutterMode flutterFlags flutterBuildFlags;
flutterMode = flutterMode';
flutterFlags = flutterFlags;
flutterBuildFlags = flutterBuildFlags';
sdkSetupScript = ''
# Pub needs SSL certificates. Dart normally looks in a hardcoded path.
@ -128,8 +158,6 @@ let
};
in
{
inherit universal;
linux = universal // {
outputs = universal.outputs or [ ] ++ [ "debug" ];
@ -225,21 +253,4 @@ let
};
}
.${targetFlutterPlatform} or (throw "Unsupported Flutter host platform: ${targetFlutterPlatform}");
minimalFlutter = flutter.override {
supportedTargetFlutterPlatforms = [
"universal"
targetFlutterPlatform
];
};
buildAppWith = flutter: buildDartApplication.override { dart = flutter; };
in
buildAppWith minimalFlutter (
builderArgs
// {
passthru = builderArgs.passthru or { } // {
multiShell = buildAppWith flutter builderArgs;
};
}
)
}