about summary refs log tree commit diff
path: root/nixpkgs/pkgs/build-support/flutter/default.nix
blob: a0ed1211ed812c5b29a4840d2b75450a851ea055 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
{ lib
, callPackage
, stdenvNoCC
, runCommand
, makeWrapper
, wrapGAppsHook
, llvmPackages_13
, cacert
, glib
, flutter
, jq
}:

# absolutely no mac support for now

{ pubGetScript ? "flutter pub get"
, flutterBuildFlags ? [ ]
, runtimeDependencies ? [ ]
, customPackageOverrides ? { }
, autoDepsList ? false
, depsListFile ? null
, vendorHash ? ""
, pubspecLockFile ? null
, nativeBuildInputs ? [ ]
, preUnpack ? ""
, postFixup ? ""
, extraWrapProgramArgs ? ""
, ...
}@args:
let
  flutterSetupScript = ''
    # 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
  '';

  deps = callPackage ../dart/fetch-dart-deps { dart = flutter; } {
    sdkSetupScript = flutterSetupScript;
    inherit pubGetScript vendorHash pubspecLockFile;
    buildDrvArgs = args;
  };

  baseDerivation = llvmPackages_13.stdenv.mkDerivation (finalAttrs: args // {
    inherit flutterBuildFlags runtimeDependencies;

    outputs = [ "out" "debug" ];

    nativeBuildInputs = [
      makeWrapper
      deps
      flutter
      jq
      glib
      wrapGAppsHook
    ] ++ nativeBuildInputs;

    dontWrapGApps = true;

    preUnpack = ''
      ${lib.optionalString (!autoDepsList) ''
        if ! { [ '${lib.boolToString (depsListFile != null)}' = 'true' ] ${lib.optionalString (depsListFile != null) "&& cmp -s <(jq -Sc . '${depsListFile}') <(jq -Sc . '${finalAttrs.passthru.depsListFile}')"}; }; then
          echo 1>&2 -e '\nThe dependency list file was either not given or differs from the expected result.' \
                      '\nPlease choose one of the following solutions:' \
                      '\n - Duplicate the following file and pass it to the depsListFile argument.' \
                      '\n   ${finalAttrs.passthru.depsListFile}' \
                      '\n - Set autoDepsList to true (not supported by Hydra or permitted in Nixpkgs)'.
          exit 1
        fi
      ''}

      ${preUnpack}
    '';

    configurePhase = ''
      runHook preConfigure

      ${flutterSetupScript}

      runHook postConfigure
    '';

    buildPhase = ''
      runHook preBuild

      mkdir -p build/flutter_assets/fonts

      doPubGet flutter pub get --offline -v
      flutter build linux -v --release --split-debug-info="$debug" ${builtins.concatStringsSep " " (map (flag: "\"${flag}\"") finalAttrs.flutterBuildFlags)}

      runHook postBuild
    '';

    installPhase = ''
      runHook preInstall

      built=build/linux/*/release/bundle

      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 -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 {} +

      # 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
    '';

    postFixup = ''
      # 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.
      for f in "$out"/bin/*; do
        wrapProgram "$f" \
          --suffix LD_LIBRARY_PATH : '${lib.makeLibraryPath finalAttrs.runtimeDependencies}' \
          ''${gappsWrapperArgs[@]} \
          ${extraWrapProgramArgs}
      done

      ${postFixup}
    '';

    passthru = (args.passthru or {}) // {
      inherit (deps) depsListFile;
    };
  });

  packageOverrideRepository = (callPackage ../../development/compilers/flutter/package-overrides { }) // customPackageOverrides;
  productPackages = builtins.filter (package: package.kind != "dev")
    (if autoDepsList
    then lib.importJSON deps.depsListFile
    else
      if depsListFile == null
      then [ ]
      else lib.importJSON depsListFile);
in
builtins.foldl'
  (prev: package:
  if packageOverrideRepository ? ${package.name}
  then
    prev.overrideAttrs
      (packageOverrideRepository.${package.name} {
        inherit (package)
          name
          version
          kind
          source
          dependencies;
      })
  else prev)
  baseDerivation
  productPackages