Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rtfm: init at 0.2.2 #244326

Merged
merged 1 commit into from
Jul 28, 2023
Merged

rtfm: init at 0.2.2 #244326

merged 1 commit into from
Jul 28, 2023

Conversation

sund3RRR
Copy link
Contributor

Description of changes

It's a dash/docset reader with built in documentation for Crystal and GTK APIs. It's written in Crystal using GTK4 bindings.
https://github.com/hugopl/rtfm

Things done
  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandbox = true set in nix.conf? (See Nix manual)
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 23.11 Release Notes (or backporting 23.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

@sund3RRR sund3RRR changed the title rtfm: init ad 0.2.1 rtfm: init at 0.2.1 Jul 19, 2023
@sund3RRR sund3RRR marked this pull request as draft July 19, 2023 15:06
@sund3RRR sund3RRR marked this pull request as ready for review July 19, 2023 15:37
@sund3RRR
Copy link
Contributor Author

@drupol, @SuperSandro2000 could you please review this PR? It's been 3 days and no one wants to :(

stdenv.mkDerivation (finalAttrs: {
name = "gtk-docs";

adw1_ver = "1.3.3";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
adw1_ver = "1.3.3";
adw1_version = "1.3.3";

adw1_docs = fetchFromGitHub {
owner = "GNOME";
repo = "libadwaita";
rev = "refs/tags/${finalAttrs.adw1_ver}";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rev = "refs/tags/${finalAttrs.adw1_ver}";
rev = "refs/tags/${finalAttrs.adw1_version}";

hash = "sha256-YIxGwl+/F7xkGjoi07GViSHAfCTE1RpEBhHfrlD0X/4=";
};

gtk4_ver = "4.11.4";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
gtk4_ver = "4.11.4";
gtk4_version = "4.11.4";

gtk4_docs = fetchFromGitHub {
owner = "GNOME";
repo = "gtk";
rev = "refs/tags/${finalAttrs.gtk4_ver}";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rev = "refs/tags/${finalAttrs.gtk4_ver}";
rev = "refs/tags/${finalAttrs.gtk4_version}";

hash = "sha256-YobWcLJm8owjrz6c6aPMCrVZqYDvNpjIt5Zea2CtAZY=";
};

pango_ver = "1.50.14";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pango_ver = "1.50.14";
pango_version = "1.50.14";

pango_docs = fetchFromGitHub {
owner = "GNOME";
repo = "pango";
rev = "refs/tags/${finalAttrs.pango_ver}";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rev = "refs/tags/${finalAttrs.pango_ver}";
rev = "refs/tags/${finalAttrs.pango_version}";

};

patches = [
./make.patch
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add a comment on why this patch is required, for each patch in this list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

ln -s ${gtk-docs} gtk-docs
'';

meta = with lib; {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add the platforms attribute with proper values? So we know on which architecture this software is available.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can i set platforms = [ "i686-linux" "x86_64-linux" ];; because i can't test on aarch64 and darwin?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just set the platforms the software you're trying to add supports, then the CI will test on these architectures.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I set platforms.linux. Theoretically, it might run on darwin, maybe i'll setup OpenCore later on my PC.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or you can ask to the Darwin maintainers team, they are quite reactive. Set it correctly and we'll ask them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. if I understand correctly how to do it

platforms = platforms.linux ++ platforms.darwin;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

@sund3RRR sund3RRR requested a review from drupol July 22, 2023 12:52
@ofborg ofborg bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin and removed 10.rebuild-darwin: 1 10.rebuild-darwin: 1-10 labels Jul 22, 2023
@sund3RRR sund3RRR force-pushed the rtfm-init branch 2 times, most recently from 785ab0d to 60fb6d6 Compare July 22, 2023 14:10
@ofborg ofborg bot added 10.rebuild-darwin: 1-10 10.rebuild-darwin: 1 and removed 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin labels Jul 22, 2023
cp -r * $out

runHook postInstall
'';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also get a meta section

buildInputs = [ gobject-introspection libadwaita gtk4 pango ];

doCheck = false;
doInstallCheck = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
doInstallCheck = false;

only one should be required here

dontUnpack = true;

nativeBuildInputs = [ gi-docgen ];
buildInputs = [ gobject-introspection libadwaita gtk4 pango ];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gobject-introspection goes to nativeBuildInputs

Comment on lines 20 to 36
gtk4_version = "4.11.4";
gtk4_docs = fetchFromGitHub {
owner = "GNOME";
repo = "gtk";
rev = "refs/tags/${finalAttrs.gtk4_version}";
hash = "sha256-YobWcLJm8owjrz6c6aPMCrVZqYDvNpjIt5Zea2CtAZY=";
};

pango_version = "1.50.14";
pango_docs = fetchFromGitHub {
owner = "GNOME";
repo = "pango";
rev = "refs/tags/${finalAttrs.pango_version}";
hash = "sha256-b3N9kM8xZ/FkpPR9gYJz96tiG/XUOUB6ipfLmBllCKc=";
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really must pin those 3?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, because they are marked as dependencies on the project's github, that is, the author assumes that during the process of building the program, all the documentation is present in the system.

- Offline crystal api documentation for Crystal docset generation.
- Offline Gtk api documentation for Gtk4, Gdk4, Gsk4, libAdwaita and Pango libraries for GTK4 libraries docset generation.

We can build a program without documentation at all, but in this case rtfm will be useless. It also makes no sense to set part of the documentation, since in this case the user himself will have to generate docsets from sources.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't mean that we could remove them but if we could use the versions from nixpkgs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find docs in gtk/pango/libadwaita nix packages.

, pango
}:
stdenv.mkDerivation (finalAttrs: {
name = "gtk-docs";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should get pname+version even if version is just dummy or from rtfm or so

description = "Read the Formidable Manual is a dash/docset reader with built in documentation for Crystal and GTK APIs.";
homepage = "https://github.com/hugopl/rtfm/";
license = licenses.mit;
mainProgram = "rtfm";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mainProgram = "rtfm";

that's already the default

homepage = "https://github.com/hugopl/rtfm/";
license = licenses.mit;
mainProgram = "rtfm";
platforms = platforms.linux ++ platforms.darwin;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
platforms = platforms.linux ++ platforms.darwin;

please check if buildCrystalPackage implies this

doCheck = false;

preBuild = ''
tar -xf ${crystal-docs} && mv crystal-${crystal_doc_version}-docs/ crystal-docs/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tar -xf ${crystal-docs} && mv crystal-${crystal_doc_version}-docs/ crystal-docs/
tar -xf ${crystal-docs}
mv crystal-${crystal_doc_version}-docs/ crystal-docs/

, gi-crystal
}:
let
gtk-docs = callPackage ./gtk-docs.nix { };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we passthru this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like this?

let
  passthru = {
    gtk-docs = callPackage ./gtk-docs.nix { };
  };
in

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normally we inherit into passthru so that overriding passthru is not changing things

@sund3RRR sund3RRR force-pushed the rtfm-init branch 2 times, most recently from 127f6dc to 68d72f7 Compare July 24, 2023 09:11
@sund3RRR sund3RRR changed the title rtfm: init at 0.2.1 rtfm: init at 0.2.2 Jul 24, 2023
@sund3RRR
Copy link
Contributor Author

@hugopl could you help with one point? I generate gi docs using gi-docgen. The GDK/GTK docs have a dependencies block, that has hard-coded dependency URLs.

Is there a way to change Browse documentation link to local docsets?

  [dependencies."GObject-2.0"]
  name = "GObject"
  description = "The base type system library"
  docs_url = "https://docs.gtk.org/gobject/"

  [dependencies."Gio-2.0"]
  name = "Gio"
  description = "GObject Interfaces and Objects, Networking, IPC, and I/O"
  docs_url = "https://docs.gtk.org/gio/"

  [dependencies."cairo-1.0"]
  name = "cairo"
  description = "A 2D graphics library with support for multiple output devices"
  docs_url = "https://www.cairographics.org/manual/"
  ...

Screenshot from 2023-07-24 13-31-29

@sund3RRR
Copy link
Contributor Author

After force-pushing last commit, i noticed some checks were failed.

ofborg-eval — This PR does not cleanly list package outputs after merging.
 error: attribute 'devdoc' missing

       at /var/lib/ofborg/checkout/repo/38dca4e3aa6bca43ea96d2fcc04e8229/mr-est/ofborg-evaluator-7/pkgs/applications/misc/rtfm/default.nix:67:20:

           66|
           67|     for file in "${gtk4.devdoc}"/share/doc/*; do
             |                    ^
           68|       ln -s "$file" "gtk-doc/$(basename "$file")"

It happens because gtk4.devdoc output depends in x11Support optional argument

outputs = [ "out" "dev" ] ++ lib.optionals x11Support [ "devdoc" ];

So, maybe i need to override x11Support = true?

@SuperSandro2000
Copy link
Member

So, maybe i need to override x11Support = true?

seems like it

@sund3RRR
Copy link
Contributor Author

So, maybe i need to override x11Support = true?

seems like it

Nothing happened, i don't know why and how to fix it

Comment on lines 42 to 43
substituteInPlace Makefile --replace "crystal run src/create_crystal_docset.cr" "crystal src/create_crystal_docset.cr ${crystal}/share/doc/crystal/api/"
substituteInPlace Makefile --replace "crystal run src/create_gtk_docset.cr" "crystal src/create_gtk_docset.cr gtk-doc/"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
substituteInPlace Makefile --replace "crystal run src/create_crystal_docset.cr" "crystal src/create_crystal_docset.cr ${crystal}/share/doc/crystal/api/"
substituteInPlace Makefile --replace "crystal run src/create_gtk_docset.cr" "crystal src/create_gtk_docset.cr gtk-doc/"
substituteInPlace Makefile \
--replace "crystal run src/create_crystal_docset.cr" "crystal src/create_crystal_docset.cr ${crystal}/share/doc/crystal/api/" \
--replace "crystal run src/create_gtk_docset.cr" "crystal src/create_gtk_docset.cr gtk-doc/"

@hugopl
Copy link

hugopl commented Jul 25, 2023

Is there a way to change Browse documentation link to local docsets?

I can reproduce this on Archlinux too and created an issue.

webkitgtk_6_0
sqlite
libadwaita
(gtk4.override { x11Support = true; })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(gtk4.override { x11Support = true; })
gtk4'

, libadwaita
, gtk4
, pango
}:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}:
}:
let
gtk4' = gtk4.override { x11Support = true; };
in

Comment on lines 67 to 80
for file in "${gtk4.devdoc}"/share/doc/*; do
ln -s "$file" "gtk-doc/$(basename "$file")"
done

for file in "${pango.devdoc}"/share/doc/*; do
ln -s "$file" "gtk-doc/$(basename "$file")"
done

for file in "${libadwaita.devdoc}"/share/doc/*; do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for file in "${gtk4.devdoc}"/share/doc/*; do
ln -s "$file" "gtk-doc/$(basename "$file")"
done
for file in "${pango.devdoc}"/share/doc/*; do
ln -s "$file" "gtk-doc/$(basename "$file")"
done
for file in "${libadwaita.devdoc}"/share/doc/*; do
for file in "${gtk4'.devdoc}"/share/doc/*; do
ln -s "$file" "gtk-doc/$(basename "$file")"
done
for file in "${pango.devdoc}"/share/doc/*; do
ln -s "$file" "gtk-doc/$(basename "$file")"
done
for file in "${libadwaita.devdoc}"/share/doc/*; do

Those are taken directly from the inputs, not buildInputs where you did the override

@sund3RRR
Copy link
Contributor Author

@SuperSandro2000 it works, all checks passed

@hugopl
Copy link

hugopl commented Jul 28, 2023

@sund3RRR would you mind in creating a PR for rtfm README with the instructions to install this NixOS package?

@sund3RRR
Copy link
Contributor Author

@sund3RRR would you mind in creating a PR for rtfm README with the instructions to install this NixOS package?

Sure, but these instructions will be useless until this PR is approved

@hugopl
Copy link

hugopl commented Jul 28, 2023

Yeah, sure, I mean, when it gets approved/merged.

Copy link
Contributor

@drupol drupol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution, it's ok for me now.

@drupol drupol merged commit 4511c5b into NixOS:master Jul 28, 2023

patches = [
# 1) fixed gi-crystal binding generator command
# 2) removed `-v` arg to `cp` command to prevent build failure due to stdout buffer overflow
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can fix that upstream.

# 1) fixed gi-crystal binding generator command
# 2) removed `-v` arg to `cp` command to prevent build failure due to stdout buffer overflow
# 3) added commands to build gschemas and update icon-cache
./patches/make.patch
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually distros run these command for any packages that install files on specific dirs used by these commands.

configure:
- shards install
- ./bin/gi-crystal
+ gi-crystal
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why you guys don't use the gi-crystal that the project depends on? Do you guys have a gi-crystal nix package? if so, why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I packed gi-crystal in nixpkgs when I packed the collision. You can see these PR's: #243914
#242681
There is an explanation why we decided to do it this way.

Shortly, nix collects crystal dependencies in separated derivations, then creates a symlimks on it in lib/ directory of project. I am creating a patch when packing gi-crystal to make it work with symlinks. The main problem is that all derivations in nix store are read-only and changes in one derivation will change drv hash and all hashes, that depend on it. When we run ./bin/gi-crystal it fails with error Permission denied, because it cannot write to read-only directory.

So, gi-crystal in nixpkgs works differently due to this nix features. All you need to do is add gi-crystal in build inputs, then run gi-crystal. It will scan lib/ directory with all symlinks, then generate all bindings in lib/gi-crystal directory and copy self package to this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shortly, nix collects crystal dependencies in separated derivations, then creates a symlimks on it in lib/ directory of project. I am creating a patch when packing gi-crystal to make it work with symlinks. The main problem is that all derivations in nix store are read-only and changes in one derivation will change drv hash and all hashes, that depend on it. When we run ./bin/gi-crystal it fails with error Permission denied, because it cannot write to read-only directory.

Jumping into the convo here... How about copying the gi-crystal derivation into the current rtfm directory and then use it ? That would fix both issues. You will be re-using the existing gi-cristal and it's directory is writable since you copied it into the rtfm build directory.

WDYT? Is it feasible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about copying the gi-crystal derivation into the current rtfm directory and then use it ?

This is exactly what I did when packing gi-crystal. But I actually automated this action so as not to do it constantly for different packages.

I think that's better, don't you?

https://github.com/NixOS/nixpkgs/pull/243914/files#diff-1a3ec3be7dadadd498d82613b7eaba4d02f11f8470913c5cbdb7d54932c63954R53

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean is to copy the output of gi-crystal into rtfm and then use it.
Once the compilation is over, you delete it and only copy the relevant rtfm files in $out.

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean is to copy the output of gi-crystal into rtfm and then use it.
Once the compilation is over, you delete it and only copy the relevant rtfm files in $out.

I'm sorry but I don't understand what's the difference. If we want to make it work without patching Makefile, then we need to do several steps:

mkdir lib/gi-crystal
cp -r ${gi-crystal}/* lib/gi-crystal
mkdir bin/
mv lib/gi-crystal/bin/gi-crystal bin/
chmod +w lib/gi-crystal/src/auto

I think it is just unnecessary steps to be done on each package requires gi-crystal.

And even if we do that, we still have to patch this to remove shards install command, because it will crash building due to cannot fetch remote host github.com something like this, I definitely don't remember what that error sounds like.

WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the issue to do that all the time. I prefer that than patching the src.

But anyway, I was just passing by here thinking to provides new ideas, but I see that you already studied the question so I won't bother. I never used gi-crystal this is the reason why I might be wrong too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the issue to do that all the time. I prefer that than patching the src.

But anyway, I was just passing by here thinking to provides new ideas, but I see that you already studied the question so I won't bother. I never used gi-crystal this is the reason why I might be wrong too.

Thanks for suggesting solutions to this problem. Patching sources was the reason why I didn't want to pack gi-crystal. My best idea is to make gi-crystal portable, this fits well with the nix ideology, but changes program behaviour.

Speaking of using gi-crystal, I understand the steps involved in implementing your idea. But others may not realize that you need to copy the gi-crystal directory, mv gi-crystal binary and change the write permissions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok... sad but ok.

It's weird that it needs to write in the gi-crystal directory.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you guys are re-doing what shards already does, but at a package level. I mean... if derivations means a packaged code.

- cp -rv data/Crystal.docset $(DESTDIR)$(PREFIX)/share/rtfm/docsets/
- cp -rv data/Gtk4.docset $(DESTDIR)$(PREFIX)/share/rtfm/docsets/
+ cp -r data/Crystal.docset $(DESTDIR)$(PREFIX)/share/rtfm/docsets/
+ cp -r data/Gtk4.docset $(DESTDIR)$(PREFIX)/share/rtfm/docsets/
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this change upstream at hugopl/rtfm@8833114

install -D -m0644 CHANGELOG.md $(DESTDIR)$(PREFIX)/share/doc/rtfm/CHANGELOG.md
gzip -9fn $(DESTDIR)$(PREFIX)/share/doc/rtfm/CHANGELOG.md
+ gtk4-update-icon-cache --ignore-theme-index $(PREFIX)/share/icons/hicolor
+ glib-compile-schemas $(DESTDIR)$(PREFIX)/share/glib-2.0/schemas
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't add this upstream or it will break ArchLinux package.


# added chmod +w for copied docs to prevent error:
# `Error opening file with mode 'wb': '.../style.css': Permission denied`
./patches/enable-write-permissions.patch
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain this error? why does it happen? So I can fix it upstream too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It happens because rtfm copies docs from nix derivation, but all files in nix-store are read-only, so at first we need to add write permissions to the copied file. This error is nix-specific, but it cannot break rtfm on other distros i think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants