{ description = "The PrivateStorage Mobile application"; inputs = { nixpkgs = { url = "github:NixOS/nixpkgs?ref=nixos-22.05"; }; flake-utils.url = "github:numtide/flake-utils"; pypi-deps-db = { flake = false; url = "github:DavHau/pypi-deps-db"; }; mach-nix-flake = { flake = true; url = "github:DavHau/mach-nix"; inputs = { nixpkgs.follows = "nixpkgs"; flake-utils.follows = "flake-utils"; pypi-deps-db.follows = "pypi-deps-db"; }; }; buildozerSrc = { flake = false; url = "github:PrivateStorageio/buildozer?ref=build-without-installing"; }; pythonForAndroidSrc = { flake = false; url = "github:PrivateStorageio/python-for-android?ref=work-on-nixos"; }; }; outputs = { self, nixpkgs, flake-utils, mach-nix-flake, buildozerSrc, pythonForAndroidSrc, ... }: flake-utils.lib.eachSystem [ "x86_64-linux" ] (system: let mach-nix = mach-nix-flake.lib.${system}; pkgs = import nixpkgs { inherit system; config = { allowUnfree = true; android_sdk.accept_license = true; }; }; lib = pkgs.lib; python-for-android = mach-nix.buildPythonPackage { src = pythonForAndroidSrc; requirementsExtra = '' sh>=1.10 ''; }; buildozer = mach-nix.buildPythonPackage { src = buildozerSrc; packagesExtra = [ ]; }; kivy-env = mach-nix.mkPython { python = "python39"; requirements = '' kivy Pillow cython ''; packagesExtra = [ python-for-android buildozer ]; }; # THESE MUST AGREE WITH app/app/build.gradle # # If they do not you will get failures that look something like: # FAILURE: Build failed with an exception. # # * What went wrong: # Could not determine the dependencies of task ':app:compileReleaseJavaWithJavac'. # > Failed to install the following SDK components: # platforms;android-32 Android SDK Platform 32 # The SDK directory is not writable (/nix/store/46214a16f22rd7q8vkrhsa907ra0405l-androidsdk/libexec/android-sdk) platformVersion = "31"; cmakeVersion = "3.10.2"; buildToolsVersion = "31.0.0"; # Check out pkgs/development/mobile/androidenv/repo.json for # nixpkgs-supported versions. ndkVersion = "23.0.7123448-rc1"; # Also includes ndk androidComposition = pkgs.callPackage ./android.nix { inherit cmakeVersion buildToolsVersion platformVersion ndkVersion; }; getBuildInputs = getInputs: xs: builtins.foldl' (accum: drv: (getInputs drv) ++ accum) [] xs; # Make sure gradle uses the aapt2 from our Android SDK. Otherwise it # downloads one from the internet that doesn't work because it # specifies an interpreter (ld.so) that nothing has ensured will # exist. # # Without this you get a stack of build errors that look a bit like: # # Execution failed for task ':app:processDebugResources'. # > Could not resolve all files for configuration ':app:debugRuntimeClasspath'. # > Failed to transform appcompat-1.3.0.aar (androidx.appcompat:appcompat:1.3.0) to match attributes {artifactType=android-compiled-dependencies-resources, org.gradle.category=library, org.gradle.dependency.bundling=external, org.gradle.libraryelements=aar, org.gradle.status=release, org.gradle.usage=java-runtime}. # > Execution failed for AarResourcesCompilerTransform: /home/exarkun/.gradle/caches/transforms-3/f5908003b5fc10a9cf0fd543db10ae32/transformed/appcompat-1.3.0. # > AAPT2 aapt2-7.2.1-7984345-linux Daemon #1: Daemon startup failed # This should not happen under normal circumstances, please file an issue if it does. ANDROIDSDK = "${androidComposition.androidsdk}/libexec/android-sdk"; gradle-wrapper = pkgs.writeShellApplication { name = "gradle"; text = '' ${pkgs.gradle}/bin/gradle -Pandroid.aapt2FromMavenOverride=${ANDROIDSDK}/build-tools/${buildToolsVersion}/aapt2 "$@" ''; }; in { # packages.host-python = (import nixpkgs { system = "aarch64-linux-android"; }).python3Minimal.override { self = self.packages.host-python; }; packages.apk = pkgs.androidenv.buildApp { name = "PrivateStorage Mobile"; src = ./app; release = false; platformVersions = [ platformVersion ]; includeNDK = false; }; apps.emulate = { type = "app"; program = let emulator = pkgs.androidenv.emulateApp { name = "PrivateStorage Mobile"; inherit platformVersion; abiVersion = "x86_64"; systemImageType = "default"; app = self.packages.${system}.apk; package = "MyApp"; activity = "MainActivity"; }; in "${emulator}/bin/run-test-emulator"; }; devShells.gradle = pkgs.mkShell rec { name = "nix-native"; version = "2022.11.09"; inherit ANDROIDSDK; ANDROIDNDK = "${ANDROIDSDK}/ndk-bundle"; ANDROID_SDK_ROOT = ANDROIDSDK; ANDROID_NDK_ROOT = ANDROIDNDK; # Deprecated but ... :shrug: ANDROID_HOME = ANDROIDSDK; ANDROID_PLATFORM_VERSION = "${platformVersion}"; # https://nix.dev/tutorials/cross-compilation buildInputs = with pkgs; [ gradle-wrapper ant android-studio android-tools (python310.withPackages (ps: with ps; [ build ])) ]; shellHook = '' # Tell the tools a third time where the SDK and NDK are to be found. cat >app/local.properties <<EOF # THIS FILE IS AUTOMATICALLY GENERATED # DO NOT EDIT # YOUR CHANGES WILL BE DESTROYED sdk.dir=${ANDROIDSDK} EOF ''; }; devShells.default = self.devShells.${system}.gradle; # https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/android.section.md # is a good reference for this stuff. devShells.kivy = pkgs.mkShell rec { name = "ps-mobile"; version = "2022.10.21"; # The docs talk about ANDROID_SDK_ROOT but apparently it has been # renamed yet again. ANDROIDSDK = "${androidComposition.androidsdk}/libexec/android-sdk"; ANDROIDNDK = "${ANDROIDSDK}/ndk-bundle"; shellHook = '' export PATH="$(echo "$ANDROIDSDK/cmake/${cmakeVersion}".*/bin):$PATH" ''; buildInputs = with pkgs; [ android-studio android-tools kivy-env # Kivy and python-for-android depend on: # https://github.com/kivy/python-for-android/blob/v2020.04.29/doc/source/troubleshooting.rst#errors-related-to-java-version openjdk8 ant # python-for-android recipes all want to download and unpack their # own sources! aaaaaa unzip ] ++ # Also we need build dependencies for all of the things it is going to # build! (getBuildInputs (drv: drv.buildInputs) [ openssl libffi python39 ]); nativeBuildInputs = with pkgs; getBuildInputs (drv: drv.nativeBuildInputs) [ openssl libffi python39 ]; }; }) // { # Python cross-compilation notes. # # You can get a lot of cross-compiled stuff out of nixpkgs beneath # `pkgsCross`. For example, # # pkgsCross.aarch64-android.python3Minimal # # is just what the name says. This builds fine with only one small # change against current nixpkgs. However, its extension modules have a # harder time. For example, # # pkgsCross.aarch64-android.python3Minimal.pkgs.zfec # # fails to build because: # # running bdist_wheel # Traceback (most recent call last): # File "/build/wheel-0.37.1-source/nix_run_setup", line 8, in <module> # exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec')) # File "setup.py", line 4, in <module> # setup(maintainer=u'Alex Grönholm') # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/__init__.py", line 87, in setup # return distutils.core.setup(**attrs) # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/_distutils/core.py", line 185, in setup # return run_commands(dist) # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/_distutils/core.py", line 201, in run_commands # dist.run_commands() # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/_distutils/dist.py", line 973, in run_commands # self.run_command(cmd) # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/dist.py", line 1217, in run_command # super().run_command(command) # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/_distutils/dist.py", line 992, in run_command # cmd_obj.run() # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/wheel/bdist_wheel.py", line 295, in run # build_ext = self.reinitialize_command('build_ext') # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/__init__.py", line 200, in reinitialize_command # cmd = _Command.reinitialize_command(self, command, reinit_subcommands) # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/_distutils/cmd.py", line 312, in reinitialize_command # return self.distribution.reinitialize_command(command, reinit_subcommands) # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/_distutils/dist.py", line 945, in reinitialize_command # command = self.get_command_obj(command_name) # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/_distutils/dist.py", line 864, in get_command_obj # klass = self.get_command_class(command) # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/dist.py", line 954, in get_command_class # self.cmdclass[command] = cmdclass = ep.load() # File "/nix/store/537cvh8mqdaglc0an8ndmzszllh1lwac-python3-minimal-3.10.8/lib/python3.10/importlib/metadata/__init__.py", line 171, in load # module = import_module(match.group('module')) # File "/nix/store/537cvh8mqdaglc0an8ndmzszllh1lwac-python3-minimal-3.10.8/lib/python3.10/importlib/__init__.py", line 126, in import_module # return _bootstrap._gcd_import(name[level:], package, level) # File "<frozen importlib._bootstrap>", line 1050, in _gcd_import # File "<frozen importlib._bootstrap>", line 1027, in _find_and_load # File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked # File "<frozen importlib._bootstrap>", line 688, in _load_unlocked # File "<frozen importlib._bootstrap_external>", line 883, in exec_module # File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/command/build_ext.py", line 26, in <module> # get_config_var("LDSHARED") # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/_distutils/sysconfig.py", line 574, in get_config_var # return get_config_vars().get(name) # File "/nix/store/azdg3l16q2d03g8drgr94bg1069mfzpq-python3.10-bootstrapped-pip-22.2.2/lib/python3.10/site-packages/setuptools/_distutils/sysconfig.py", line 553, in get_config_vars # _config_vars = sysconfig.get_config_vars().copy() # File "/nix/store/537cvh8mqdaglc0an8ndmzszllh1lwac-python3-minimal-3.10.8/lib/python3.10/sysconfig.py", line 615, in get_config_vars # _init_posix(_CONFIG_VARS) # File "/nix/store/537cvh8mqdaglc0an8ndmzszllh1lwac-python3-minimal-3.10.8/lib/python3.10/sysconfig.py", line 477, in _init_posix # _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) # ModuleNotFoundError: No module named '_sysconfigdata__linux_aarch64-linux-android' # # Notice that the *build* Python # (537cvh8mqdaglc0an8ndmzszllh1lwac-python3-minimal-3.10.8) is trying to # load sysconfigdata for the *host* Python - presumably so it can # compile the extension correctly. However, the host Python # sysconfigdata module appears to be unavailable to the build Python. # # Why? The nixpkgs CPython expression writes the sysconfigdata module # for both Pythons (build and host). It has ``sysconfigdataHook`` that # goes to a lot of trouble to compute the right parameters for the host # Python and write the data to a properly named source file. # # However, the build Python and host Pythons each only get one of these # modules. For the build Python it is: # # _sysconfigdata__linux_x86_64-linux-gnu.py # # and the host Python gets: # # _sysconfigdata__linux_.py # # Each of these contains information which is apparently correct for # their respective runtime systems. Then, the nixpkgs CPython # expression for building the extension module uses "bootstrap pip" # which will respect PYTHONPATH for module search. # # (Q) Does something put the host Python's _sysconfigdata__linux_.py on # the build Python's PYTHONPATH? What? How? # # (Q) # # And now for something completely different. # # https://crossenv.readthedocs.io/en/latest/ is a tool specifically for # this. It assumes you can cross-compile CPython on your own, though, # and then helps you use it to cross-compile extension modules. Maybe # our nixpkgs-cross-compiled CPython would be a good complement? # # packages.host-python = pkgsCross.aarch64-android.python3Minimal.withPackages (ps: [ # # The nixpkgs zfec has some extra dependencies that we might not # # really care about. This one is leaner. Mostly only relevant # # because we have to cross-compile anything here. # (ps.callPackage ./zfec.nix {}) # ]); # packages.aarch64-android.host-python310 = # let # pkgs = import nixpkgs { # system = "x86_64-linux"; # config = { # allowUnfree = true; # android_sdk.accept_license = true; # }; # }; # in # (pkgs.pkgsCross.aarch64-android.python310.override { # # Turn off all the things. Each one might be broken for # # cross-compilation and even if not we don't need them for this build # # Python. We just need it to report the right platform info. # mimetypesSupport = false; # x11Support = false; # bluezSupport = false; # sqlite = null; # openssl = null; # tzdata = null; # readline = null; # }).overrideAttrs (old: { # # Turn off even more things which aren't parameters to the Python # # expressionThey . # # These drag in an aarch64-android bash which fails to build. # preFixup = null; # postPatch = null; # # This drags in a -lcrypt dependency which isn't satisfied by Google # # Android Bionic. # LIBS = null; # }); }; }