summary refs log tree commit diff
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2024-09-01 18:10:26 +0300
committerVika <vika@fireburn.ru>2024-09-04 19:51:50 +0300
commitf8d61d957d4a2d086f4b97f2e3d7d19d0bb35f13 (patch)
tree740988c295d5e3c53b93bd09bad0ce73211b2f40
parent7718901fb8346a75bd203f04e4303d0df007dcd1 (diff)
downloadbowl-f8d61d957d4a2d086f4b97f2e3d7d19d0bb35f13.tar.zst
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.
-rw-r--r--.gitignore1
-rw-r--r--build-aux/dist-vendor.sh35
-rw-r--r--build.rs17
-rw-r--r--data/meson.build75
-rw-r--r--data/xyz.vikanezrimaya.kittybox.Bowl.desktop.in.in12
-rw-r--r--default.nix41
-rw-r--r--flake.nix2
-rw-r--r--meson.build68
-rw-r--r--meson.options10
-rw-r--r--po/LINGUAS0
-rw-r--r--po/POTFILES.in18
-rw-r--r--po/meson.build1
-rw-r--r--src/meson.build36
13 files changed, 310 insertions, 6 deletions
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
--- /dev/null
+++ b/po/LINGUAS
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@',
+  ]
+)