Merge pull request #281662 from hacker1024/feature/flutter-build-web

Support Web builds in buildFlutterApplication
This commit is contained in:
Maciej Krüger 2024-01-19 23:56:52 +01:00 committed by GitHub
commit 45308f5528
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 210 additions and 133 deletions

View File

@ -80,6 +80,8 @@ Do _not_ use `dart run <package_name>`, as this will attempt to download depende
### Usage with nix-shell {#ssec-dart-applications-nix-shell}
#### Using dependencies from the Nix store {#ssec-dart-applications-nix-shell-deps}
As `buildDartApplication` provides dependencies instead of `pub get`, Dart needs to be explicitly told where to find them.
Run the following commands in the source directory to configure Dart appropriately.
@ -103,6 +105,9 @@ flutter.buildFlutterApplication {
pname = "firmware-updater";
version = "unstable-2023-04-30";
# To build for the Web, use the targetFlutterPlatform argument.
# targetFlutterPlatform = "web";
src = fetchFromGitHub {
owner = "canonical";
repo = "firmware-updater";
@ -117,4 +122,15 @@ flutter.buildFlutterApplication {
### Usage with nix-shell {#ssec-dart-flutter-nix-shell}
See the [Dart documentation](#ssec-dart-applications-nix-shell) for nix-shell instructions.
Flutter-specific `nix-shell` usage notes are included here. See the [Dart documentation](#ssec-dart-applications-nix-shell) for general `nix-shell` instructions.
#### Entering the shell {#ssec-dart-flutter-nix-shell-enter}
By default, dependencies for only the `targetFlutterPlatform` are available in the
build environment. This is useful for keeping closures small, but be problematic
during development. It's common, for example, to build Web apps for Linux during
development to take advantage of native features such as stateful hot reload.
To enter a shell with all the usual target platforms available, use the `multiShell` attribute.
e.g. `nix-shell '<nixpkgs>' -A fluffychat-web.multiShell`.

View File

@ -1,4 +1,5 @@
{ lib
, fetchzip
, fetchFromGitHub
, imagemagick
, mesa
@ -7,13 +8,15 @@
, pulseaudio
, makeDesktopItem
, gnome
, targetFlutterPlatform ? "linux"
}:
let
libwebrtcRpath = lib.makeLibraryPath [ mesa libdrm ];
in
flutter313.buildFlutterApplication rec {
pname = "fluffychat";
flutter313.buildFlutterApplication (rec {
pname = "fluffychat-${targetFlutterPlatform}";
version = "1.14.1";
src = fetchFromGitHub {
@ -30,6 +33,25 @@ flutter313.buildFlutterApplication rec {
wakelock_windows = "sha256-Dfwe3dSScD/6kvkP67notcbb+EgTQ3kEYcH7wpra2dI=";
};
inherit targetFlutterPlatform;
meta = with lib; {
description = "Chat with your friends (matrix client)";
homepage = "https://fluffychat.im/";
license = licenses.agpl3Plus;
maintainers = with maintainers; [ mkg20001 gilice ];
platforms = [ "x86_64-linux" "aarch64-linux" ];
sourceProvenance = [ sourceTypes.fromSource ];
};
} // lib.optionalAttrs (targetFlutterPlatform == "linux") {
nativeBuildInputs = [ imagemagick ];
runtimeDependencies = [ pulseaudio ];
extraWrapProgramArgs = "--prefix PATH : ${gnome.zenity}/bin";
env.NIX_LDFLAGS = "-rpath-link ${libwebrtcRpath}";
desktopItem = makeDesktopItem {
name = "Fluffychat";
exec = "@out@/bin/fluffychat";
@ -39,9 +61,6 @@ flutter313.buildFlutterApplication rec {
categories = [ "Chat" "Network" "InstantMessaging" ];
};
nativeBuildInputs = [ imagemagick ];
runtimeDependencies = [ pulseaudio ];
extraWrapProgramArgs = "--prefix PATH : ${gnome.zenity}/bin";
postInstall = ''
FAV=$out/app/data/flutter_assets/assets/favicon.png
ICO=$out/share/icons
@ -59,15 +78,21 @@ flutter313.buildFlutterApplication rec {
patchelf --add-rpath ${libwebrtcRpath} $out/app/lib/libwebrtc.so
'';
env.NIX_LDFLAGS = "-rpath-link ${libwebrtcRpath}";
meta = with lib; {
description = "Chat with your friends (matrix client)";
homepage = "https://fluffychat.im/";
license = licenses.agpl3Plus;
maintainers = with maintainers; [ mkg20001 gilice ];
platforms = [ "x86_64-linux" "aarch64-linux" ];
sourceProvenance = [ sourceTypes.fromSource ];
};
}
} // lib.optionalAttrs (targetFlutterPlatform == "web") {
prePatch =
# https://github.com/krille-chan/fluffychat/blob/v1.17.1/scripts/prepare-web.sh
let
# Use Olm 1.3.2, the oldest version, for FluffyChat 1.14.1 which depends on olm_flutter 1.2.0.
# In the future, this should be changed to use self.pubspecLock.dependencyVersions.flutter_olm as the script does.
olmVersion = "1.3.2";
olmJs = fetchzip {
url = "https://github.com/famedly/olm/releases/download/v${olmVersion}/olm.zip";
stripRoot = false;
hash = "sha256-Vl3Cp2OaYzM5CPOOtTHtUb1W48VXePzOV6FeiIzyD1Y=";
};
in
''
rm -r assets/js/package
cp -r '${olmJs}/javascript' assets/js/package
'';
})

View File

@ -87,7 +87,7 @@ let
dartCompileCommand dartOutputType dartRuntimeCommand dartCompileFlags
dartJitFlags;
outputs = args.outputs or [ ] ++ [ "out" "pubcache" ];
outputs = [ "out" "pubcache" ] ++ args.outputs or [ ];
dartEntryPoints =
if (dartEntryPoints != null)

View File

@ -19,15 +19,25 @@ dartInstallHook() {
fi
done < <(_getDartEntryPoints)
# Install the package_config.json file.
mkdir -p "$pubcache"
cp .dart_tool/package_config.json "$pubcache/package_config.json"
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

View File

@ -17,134 +17,163 @@
{ pubGetScript ? "flutter pub get"
, flutterBuildFlags ? [ ]
, targetFlutterPlatform ? "linux"
, extraWrapProgramArgs ? ""
, ...
}@args:
(buildDartApplication.override {
dart = flutter;
}) (args // {
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"
let
builderArgs = rec {
universal = args // {
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 --no-analytics &>/dev/null # mute first-run
flutter config --enable-linux-desktop >/dev/null
'';
export HOME="$NIX_BUILD_TOP"
flutter config --no-analytics &>/dev/null # mute first-run
flutter config --enable-linux-desktop >/dev/null
'';
inherit pubGetScript;
inherit pubGetScript;
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
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
'';
};
extraPackageConfigSetup = ''
# https://github.com/flutter/flutter/blob/3.13.8/packages/flutter_tools/lib/src/dart/pub.dart#L755
if [ "$('${yq}/bin/yq' '.flutter.generate // false' pubspec.yaml)" = "true" ]; then
'${jq}/bin/jq' '.packages |= . + [{
name: "flutter_gen",
rootUri: "flutter_gen",
languageVersion: "2.12",
}]' "$out" | '${moreutils}/bin/sponge' "$out"
fi
done
'';
};
if [ ! -e "$out" ]; then
echo 1>&2 'The Flutter SDK does not contain the requested package: ${name}!'
exit 1
fi
'';
};
linux = universal // {
outputs = universal.outputs or [ ] ++ [ "debug" ];
extraPackageConfigSetup = ''
# https://github.com/flutter/flutter/blob/3.13.8/packages/flutter_tools/lib/src/dart/pub.dart#L755
if [ "$('${yq}/bin/yq' '.flutter.generate // false' pubspec.yaml)" = "true" ]; then
'${jq}/bin/jq' '.packages |= . + [{
name: "flutter_gen",
rootUri: "flutter_gen",
languageVersion: "2.12",
}]' "$out" | '${moreutils}/bin/sponge' "$out"
fi
'';
nativeBuildInputs = (universal.nativeBuildInputs or [ ]) ++ [
wrapGAppsHook
nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [
wrapGAppsHook
# 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
];
# 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 ];
buildInputs = (args.buildInputs or [ ]) ++ [ glib ];
dontDartBuild = true;
buildPhase = universal.buildPhase or ''
runHook preBuild
dontDartBuild = true;
buildPhase = args.buildPhase or ''
runHook preBuild
mkdir -p build/flutter_assets/fonts
mkdir -p build/flutter_assets/fonts
flutter build linux -v --release --split-debug-info="$debug" ${builtins.concatStringsSep " " (map (flag: "\"${flag}\"") flutterBuildFlags)}
flutter build linux -v --release --split-debug-info="$debug" ${builtins.concatStringsSep " " (map (flag: "\"${flag}\"") flutterBuildFlags)}
runHook postBuild
'';
runHook postBuild
'';
dontDartInstall = true;
installPhase = universal.installPhase or ''
runHook preInstall
dontDartInstall = true;
installPhase = args.installPhase or ''
runHook preInstall
built=build/linux/*/release/bundle
built=build/linux/*/release/bundle
mkdir -p $out/bin
mv $built $out/app
mkdir -p $out/bin
mv $built $out/app
for f in $(find $out/app -iname "*.desktop" -type f); do
install -D $f $out/share/applications/$(basename $f)
done
for f in $(find $out/app -iname "*.desktop" -type f); do
install -D $f $out/share/applications/$(basename $f)
done
for f in $(find $out/app -maxdepth 1 -type f); do
ln -s $f $out/bin/$(basename $f)
done
for f in $(find $out/app -maxdepth 1 -type f); do
ln -s $f $out/bin/$(basename $f)
done
# make *.so executable
find $out/app -iname "*.so" -type f -exec chmod +x {} +
# make *.so executable
find $out/app -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 -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
# remove stuff like /build/source/packages/ubuntu_desktop_installer/linux/flutter/ephemeral
for f in $(find $out/app -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
'';
# Install the package_config.json file.
# This is normally done by dartInstallHook, but we disable it.
mkdir -p "$pubcache"
cp .dart_tool/package_config.json "$pubcache/package_config.json"
dontWrapGApps = true;
extraWrapProgramArgs = ''
''${gappsWrapperArgs[@]} \
${extraWrapProgramArgs}
'';
};
runHook postInstall
'';
web = universal // {
dontDartBuild = true;
buildPhase = universal.buildPhase or ''
runHook preBuild
dontWrapGApps = true;
extraWrapProgramArgs = ''
''${gappsWrapperArgs[@]} \
${extraWrapProgramArgs}
'';
})
mkdir -p build/flutter_assets/fonts
flutter build web -v --release ${builtins.concatStringsSep " " (map (flag: "\"${flag}\"") 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; }; })

View File

@ -51,12 +51,7 @@ let
(mkCustomFlutter args).overrideAttrs (prev: next: {
passthru = next.passthru // rec {
inherit wrapFlutter mkCustomFlutter mkFlutter;
buildFlutterApplication = callPackage ../../../build-support/flutter {
# Package a minimal version of Flutter that only uses Linux desktop release artifacts.
flutter = (wrapFlutter (mkCustomFlutter args)).override {
supportedTargetFlutterPlatforms = [ "universal" "linux" ];
};
};
buildFlutterApplication = callPackage ../../../build-support/flutter { flutter = wrapFlutter (mkCustomFlutter args); };
};
});

View File

@ -3848,6 +3848,8 @@ with pkgs;
fluffychat = callPackage ../applications/networking/instant-messengers/fluffychat { };
fluffychat-web = fluffychat.override { targetFlutterPlatform = "web"; };
fxlinuxprintutil = callPackage ../tools/misc/fxlinuxprintutil { };
gbl = callPackage ../tools/archivers/gbl {