Files
nixpkgs/pkgs/development/compilers/flutter/build-support/build-flutter-application.nix
2025-08-19 06:13:41 +08:00

246 lines
7.9 KiB
Nix

{
lib,
callPackage,
runCommand,
makeWrapper,
wrapGAppsHook3,
buildDartApplication,
cacert,
glib,
flutter,
pkg-config,
buildPackages,
}:
# 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 =
let
universal = args // {
inherit flutterMode flutterFlags flutterBuildFlags;
sdkSetupScript = ''
# Pub needs SSL certificates. Dart normally looks in a hardcoded path.
# https://github.com/dart-lang/sdk/blob/3.1.0/runtime/bin/security_context_linux.cc#L48
#
# Dart does not respect SSL_CERT_FILE...
# https://github.com/dart-lang/sdk/issues/48506
# ...and Flutter does not support --root-certs-file, so the path cannot be manually set.
# https://github.com/flutter/flutter/issues/56607
# https://github.com/flutter/flutter/issues/113594
#
# libredirect is of no use either, as Flutter does not pass any
# environment variables (including LD_PRELOAD) to the Pub process.
#
# Instead, Flutter is patched to allow the path to the Dart binary used for
# Pub commands to be overriden.
export NIX_FLUTTER_PUB_DART="${
runCommand "dart-with-certs" { nativeBuildInputs = [ makeWrapper ]; } ''
mkdir -p "$out/bin"
makeWrapper ${flutter.dart}/bin/dart "$out/bin/dart" \
--add-flags "--root-certs-file=${cacert}/etc/ssl/certs/ca-bundle.crt"
''
}/bin/dart"
export HOME="$NIX_BUILD_TOP"
flutter config $flutterFlags --no-analytics &>/dev/null # mute first-run
flutter config $flutterFlags --enable-linux-desktop >/dev/null
'';
pubGetScript =
args.pubGetScript
or "flutter${lib.optionalString hasEngine " --local-engine $flutterMode"} pub get";
sdkSourceBuilders = {
# https://github.com/dart-lang/pub/blob/68dc2f547d0a264955c1fa551fa0a0e158046494/lib/src/sdk/flutter.dart#L81
"flutter" =
name:
runCommand "flutter-sdk-${name}" { passthru.packageRoot = "."; } ''
for path in '${flutter}/packages/${name}' '${flutter}/bin/cache/pkg/${name}'; do
if [ -d "$path" ]; then
ln -s "$path" "$out"
break
fi
done
if [ ! -e "$out" ]; then
echo 1>&2 'The Flutter SDK does not contain the requested package: ${name}!'
exit 1
fi
'';
# 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 '${flutter.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
'';
};
# https://github.com/flutter/flutter/blob/edada7c56edf4a183c1735310e123c7f923584f1/packages/flutter_tools/lib/src/dart/pub.dart#L804
extraPackageConfigSetup = lib.optionalString (lib.versionOlder flutter.version "3.34.0") ''
if [ "$("${lib.getExe buildPackages.yq}" '.flutter.generate // false' pubspec.yaml)" = "true" ]; then
if ! "${lib.getExe buildPackages.jq}" -e '.packages[] | select(.name == "flutter_gen")' "$out" >/dev/null 2>&1; then
export TEMP_PACKAGES=$(mktemp)
"${lib.getExe buildPackages.jq}" '.packages |= . + [{
name: "flutter_gen",
rootUri: "flutter_gen",
languageVersion: "2.12"
}]' "$out" > "$TEMP_PACKAGES"
cp "$TEMP_PACKAGES" "$out"
rm "$TEMP_PACKAGES"
unset TEMP_PACKAGES
fi
fi
'';
};
in
{
inherit universal;
linux = universal // {
outputs = universal.outputs or [ ] ++ [ "debug" ];
nativeBuildInputs = (universal.nativeBuildInputs or [ ]) ++ [
wrapGAppsHook3
# Flutter requires pkg-config for Linux desktop support, and many plugins
# attempt to use it.
#
# It is available to the `flutter` tool through its wrapper, but it must be
# added here as well so the setup hook adds plugin dependencies to the
# pkg-config search paths.
pkg-config
];
buildInputs = (universal.buildInputs or [ ]) ++ [ glib ];
dontDartBuild = true;
buildPhase =
universal.buildPhase or ''
runHook preBuild
mkdir -p build/flutter_assets/fonts
flutter build linux -v --split-debug-info="$debug" $flutterBuildFlags
runHook postBuild
'';
dontDartInstall = true;
installPhase =
universal.installPhase or ''
runHook preInstall
built=build/linux/*/$flutterMode/bundle
mkdir -p $out/bin
mkdir -p $out/app
mv $built $out/app/$pname
for f in $(find $out/app/$pname -iname "*.desktop" -type f); do
install -D $f $out/share/applications/$(basename $f)
done
for f in $(find $out/app/$pname -maxdepth 1 -type f); do
ln -s $f $out/bin/$(basename $f)
done
# make *.so executable
find $out/app/$pname -iname "*.so" -type f -exec chmod +x {} +
# remove stuff like /build/source/packages/ubuntu_desktop_installer/linux/flutter/ephemeral
for f in $(find $out/app/$pname -executable -type f); do
if patchelf --print-rpath "$f" | grep /build; then # this ignores static libs (e,g. libapp.so) also
echo "strip RPath of $f"
newrp=$(patchelf --print-rpath $f | sed -r "s|/build.*ephemeral:||g" | sed -r "s|/build.*profile:||g")
patchelf --set-rpath "$newrp" "$f"
fi
done
runHook postInstall
'';
dontWrapGApps = true;
extraWrapProgramArgs = ''
''${gappsWrapperArgs[@]} \
${extraWrapProgramArgs}
'';
};
web = universal // {
dontDartBuild = true;
buildPhase =
universal.buildPhase or ''
runHook preBuild
mkdir -p build/flutter_assets/fonts
flutter build web -v $flutterBuildFlags
runHook postBuild
'';
dontDartInstall = true;
installPhase =
universal.installPhase or ''
runHook preInstall
cp -r build/web "$out"
runHook postInstall
'';
};
}
.${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;
};
}
)