From f8d61d957d4a2d086f4b97f2e3d7d19d0bb35f13 Mon Sep 17 00:00:00 2001 From: Vika Date: Sun, 1 Sep 2024 18:10:26 +0300 Subject: Mesonify build This took a while and had me scratching my head often. But I managed to combine the best parts of Crane and Meson together, allowing me to have blazing fast Nix builds. This also adds initial scaffolding for gettext and other cool things. --- .gitignore | 1 + build-aux/dist-vendor.sh | 35 ++++++++++ build.rs | 17 +++++ data/meson.build | 75 ++++++++++++++++++++++ data/xyz.vikanezrimaya.kittybox.Bowl.desktop.in.in | 12 ++++ default.nix | 41 ++++++++++-- flake.nix | 2 + meson.build | 68 ++++++++++++++++++++ meson.options | 10 +++ po/LINGUAS | 0 po/POTFILES.in | 18 ++++++ po/meson.build | 1 + src/meson.build | 36 +++++++++++ 13 files changed, 310 insertions(+), 6 deletions(-) create mode 100644 build-aux/dist-vendor.sh create mode 100644 build.rs create mode 100644 data/meson.build create mode 100644 data/xyz.vikanezrimaya.kittybox.Bowl.desktop.in.in create mode 100644 meson.build create mode 100644 meson.options create mode 100644 po/LINGUAS create mode 100644 po/POTFILES.in create mode 100644 po/meson.build create mode 100644 src/meson.build diff --git a/.gitignore b/.gitignore index ea8c4bf..abba15c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/build \ No newline at end of file diff --git a/build-aux/dist-vendor.sh b/build-aux/dist-vendor.sh new file mode 100644 index 0000000..c1430dc --- /dev/null +++ b/build-aux/dist-vendor.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# Since Meson invokes this script as +# "/bin/sh .../dist-vendor.sh DIST SOURCE_ROOT" we can't rely on bash features +set -eu +export DIST="$1" +export SOURCE_ROOT="$2" + +cd "$SOURCE_ROOT" +mkdir "$DIST"/.cargo + +# cargo-vendor-filterer can be found at https://github.com/coreos/cargo-vendor-filterer +# It is also part of the Rust SDK extension. +# +# nixpkgs doesn't have it packaged though. +if command -v cargo-vendor-filterer &>/dev/null; then + cargo vendor-filterer \ + --platform=x86_64-unknown-linux-gnu \ + --platform=aarch64-unknown-linux-gnu \ + > "$DIST"/.cargo/config +else + echo "WARNING: using normal cargo vendor" + cargo vendor > "$DIST"/.cargo/config +fi + +# Remove vendored gettext sources, we'll be using system gettext. +rm -f vendor/gettext-sys/gettext-*.tar.* + +# Don't combine the previous and this line with a pipe because we can't catch +# errors with "set -o pipefail" +sed -i 's/^directory = ".*"/directory = "vendor"/g' "$DIST/.cargo/config" + +# Move vendor into dist tarball directory +mv vendor "$DIST" + +rm -r "$DIST"/*.nix "$DIST/pkgs" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..8113d1c --- /dev/null +++ b/build.rs @@ -0,0 +1,17 @@ +fn main() { + if std::env::var_os("PKGDATADIR").is_none() { + println!("cargo::rustc-env=PKGDATADIR={}", { + let mut path = std::path::PathBuf::from(std::env::var_os("OUTDIR").unwrap()); + path.push("share"); + path + }.display()) + } + + if std::env::var_os("LOCALEDIR").is_none() { + println!("cargo::rustc-env=PKGDATADIR={}", { + let mut path = std::path::PathBuf::from(std::env::var_os("OUTDIR").unwrap()); + path.push("locale"); + path + }.display()) + } +} diff --git a/data/meson.build b/data/meson.build new file mode 100644 index 0000000..c1d1997 --- /dev/null +++ b/data/meson.build @@ -0,0 +1,75 @@ +#subdir('icons') +#subdir('resources') + +# Desktop file +desktop_conf = configuration_data() +desktop_conf.set('icon', application_id) +desktop_file = i18n.merge_file( + type: 'desktop', + input: configure_file( + input: '@0@.desktop.in.in'.format(base_id), + output: '@BASENAME@', + configuration: desktop_conf + ), + output: '@0@.desktop'.format(application_id), + po_dir: podir, + install: true, + install_dir: datadir / 'applications' +) +# Validate Desktop file +if desktop_file_validate.found() + test( + 'validate-desktop', + desktop_file_validate, + args: [ + desktop_file.full_path() + ], + depends: desktop_file, + ) +endif + +# Appdata +#appdata_conf = configuration_data() +#appdata_conf.set('app-id', application_id) +#appdata_conf.set('gettext-package', gettext_package) +#appdata_file = i18n.merge_file( +# input: configure_file( +# input: '@0@.metainfo.xml.in.in'.format(base_id), +# output: '@BASENAME@', +# configuration: appdata_conf +# ), +# output: '@0@.metainfo.xml'.format(application_id), +# po_dir: podir, +# install: true, +# install_dir: datadir / 'metainfo' +#) +## Validate Appdata +#if appstreamcli.found() +# test( +# 'validate-appdata', appstreamcli, +# args: [ +# 'validate', '--no-net', '--explain', appdata_file.full_path() +# ], +# depends: appdata_file, +# ) +#endif + +## GSchema +#gschema_conf = configuration_data() +#gschema_conf.set('app-id', application_id) +#gschema_conf.set('gettext-package', gettext_package) +#configure_file( +# input: '@0@.gschema.xml.in'.format(base_id), +# output: '@0@.gschema.xml'.format(application_id), +# configuration: gschema_conf, +# install: true, +# install_dir: datadir / 'glib-2.0' / 'schemas' +#) +# +## Validata GSchema +#test( +# 'validate-gschema', glib_compile_schemas, +# args: [ +# '--strict', '--dry-run', meson.current_build_dir() +# ], +#) diff --git a/data/xyz.vikanezrimaya.kittybox.Bowl.desktop.in.in b/data/xyz.vikanezrimaya.kittybox.Bowl.desktop.in.in new file mode 100644 index 0000000..5eb03c5 --- /dev/null +++ b/data/xyz.vikanezrimaya.kittybox.Bowl.desktop.in.in @@ -0,0 +1,12 @@ +[Desktop Entry] +Name=Bowl +Comment=Minimalist Micropub post creator +Type=Application +Exec=bowl +Terminal=false +Categories=GNOME;Internet; +# Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! +Keywords=Micropub;IndieWeb;Kittybox; +# Translators: Do NOT translate or transliterate this text (this is an icon file name)! +Icon=@icon@ +StartupNotify=true \ No newline at end of file diff --git a/default.nix b/default.nix index e4d4f7e..c79ba7c 100644 --- a/default.nix +++ b/default.nix @@ -1,11 +1,12 @@ -{ craneLib, lib -, pkg-config, wrapGAppsHook -, gtk4, libadwaita, libpanel, libsoup_3, libsecret, librsvg, gettext +{ craneLib, lib, rustc +, pkg-config, wrapGAppsHook, meson, ninja, gettext +, desktop-file-utils +, gtk4, libadwaita, libpanel, libsoup_3, libsecret, librsvg }: let src = let - suffixes = []; + suffixes = [ "meson.options" "meson.build" ".sh" ".po" ".pot" ".in" ]; suffixFilter = name: type: let base = baseNameOf (toString name); in type == "directory" || lib.any (ext: lib.hasSuffix ext base) suffixes; @@ -30,7 +31,7 @@ let # cargoExtraArgs can be used to inject features buildInputs = [ gtk4 libadwaita libpanel libsoup_3 libsecret librsvg gettext ]; - nativeBuildInputs = [ pkg-config wrapGAppsHook gettext ]; + nativeBuildInputs = [ pkg-config wrapGAppsHook gettext meson ninja ]; meta = with lib.meta; { maintainers = with lib.maintainers; [ vikanezrimaya ]; @@ -42,7 +43,35 @@ let cargoArtifacts = craneLib.buildDepsOnly args; args' = args // { inherit cargoArtifacts; }; -in craneLib.buildPackage (args' // { +in craneLib.mkCargoDerivation (args' // { + # This is the magic sauce to integrate Crane and Meson builds + # (see `src/meson.build` for details on how we call Cargo) + + # Use source replacement so we can use the crane vendor tarball + postConfigure = '' + mkdir -p cargo-home + cp $CARGO_HOME/config.toml cargo-home/config.toml + ''; + # Move intermediate artefacts from Cargo to the Meson build folder + preBuild = '' + mv ../target/release src/release + ''; + # Use standard Meson and Ninja build phases + configurePhase = "mesonConfigurePhase"; + buildPhase = "ninjaBuildPhase"; + buildPhaseCargoCommand = "# unused"; + checkPhase = "mesonCheckPhase"; + installPhase = "mesonInstallPhase"; + + nativeBuildInputs = args'.nativeBuildInputs ++ [ + rustc # Only needed for Meson to successfully detect the Rust toolchain + + # Auxiliary packages + desktop-file-utils + ]; + + doInstallCargoArtifacts = false; + passthru = { inherit src cargoArtifacts; clippy = craneLib.cargoClippy args'; diff --git a/flake.nix b/flake.nix index 24a1f94..fad8145 100644 --- a/flake.nix +++ b/flake.nix @@ -58,6 +58,8 @@ packages = [ pkgs.rust-analyzer self.packages.${system}.xtr + + pkgs.desktop-file-utils ]; }; }); diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..9010236 --- /dev/null +++ b/meson.build @@ -0,0 +1,68 @@ +project( + 'bowl', + 'rust', + version: '0.1.0', + meson_version: '>= 1.1', + license: 'AGPLv3', +) + +i18n = import('i18n') +gnome = import('gnome') + +base_id = 'xyz.vikanezrimaya.kittybox.Bowl' + + +dependency('glib-2.0', version: '>= 2.66') +dependency('gio-2.0', version: '>= 2.66') +dependency('gtk4', version: '>= 4.14.0') +dependency('libsecret-1', version: '>= 0.21.2') +dependency('libsoup-3.0', version: '>= 3.0') + +find_program('glib-compile-resources') +glib_compile_schemas = find_program('glib-compile-schemas') +desktop_file_validate = find_program('desktop-file-validate', required: false) +appstreamcli = find_program('appstreamcli', required: false) +cargo = find_program('cargo') + +version = meson.project_version() + +prefix = get_option('prefix') +bindir = prefix / get_option('bindir') +localedir = prefix / get_option('localedir') + +datadir = prefix / get_option('datadir') +pkgdatadir = datadir / meson.project_name() +iconsdir = datadir / 'icons' +podir = meson.project_source_root() / 'po' +gettext_package = meson.project_name() + +if get_option('profile') == 'development' + profile = 'Devel' + vcs_tag = run_command('git', 'rev-parse', '--short', 'HEAD', check: false).stdout().strip() + if vcs_tag == '' + version_suffix = '-devel' + else + version_suffix = '-@0@'.format(vcs_tag) + endif + application_id = '@0@.@1@'.format(base_id, profile) +else + profile = '' + version_suffix = '' + application_id = base_id +endif + +meson.add_dist_script( + 'build-aux/dist-vendor.sh', + meson.project_build_root() / 'meson-dist' / meson.project_name() + '-' + version, + meson.project_source_root() +) + +subdir('src') +subdir('po') +subdir('data') + +gnome.post_install( + gtk_update_icon_cache: false, + glib_compile_schemas: false, + update_desktop_database: true, +) diff --git a/meson.options b/meson.options new file mode 100644 index 0000000..7decbf0 --- /dev/null +++ b/meson.options @@ -0,0 +1,10 @@ +option( + 'profile', + type: 'combo', + choices: [ + 'default', + 'development' + ], + value: 'default', + description: 'The build profile for Bowl. One of "default" or "development".' +) \ No newline at end of file diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 0000000..e69de29 diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..d7868d7 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,18 @@ +data/xyz.vikanezrimaya.kittybox.Bowl.desktop.in.in + +### Below are rust files containing translatable strings. +### +### It's better to use `xtr` instead of `xgettext` for those. +### (Rust gettext!() macro is equivalent to format!() but isn't recognized as a keyword) +# src/lib.rs +# src/components/post_editor.rs +# src/components/signin.rs +# src/components/smart_summary.rs +### +### To properly use `xtr`, do: +### +### $ xtr -o /dev/stdout src/lib.rs | cat >> po/bowl.pot +### +### because xtr truncates the file descriptor it writes to. +### +### Perhaps it would be better to produce a proper workflow for regenerating this .pot file. \ No newline at end of file diff --git a/po/meson.build b/po/meson.build new file mode 100644 index 0000000..6a87565 --- /dev/null +++ b/po/meson.build @@ -0,0 +1 @@ +i18n.gettext(gettext_package, preset: 'glib') \ No newline at end of file diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..350b911 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,36 @@ +cargo_options = [ '--manifest-path', meson.project_source_root() / 'Cargo.toml' ] +cargo_options += [ '--target-dir', meson.project_build_root() / 'src' ] + +if get_option('profile') == 'default' + cargo_options += [ '--release' ] + rust_target = 'release' + message('Building in release mode') +else + rust_target = 'debug' + message('Building in debug mode') +endif + +cargo_env = [ + 'CARGO_HOME=' + meson.project_build_root() / 'cargo-home', + 'PKGDATADIR=' + pkgdatadir, + 'LOCALEDIR=' + localedir, +] + +cargo_build = custom_target( + 'cargo-build', + build_by_default: true, + build_always_stale: true, + output: meson.project_name(), + console: true, + install: true, + install_dir: bindir, + #depends: resources, + command: [ + 'env', + cargo_env, + cargo, 'build', + cargo_options, + '&&', + 'cp', 'src' / rust_target / meson.project_name(), '@OUTPUT@', + ] +) -- cgit 1.4.1