Closed gouravkhunger closed 2 months ago
I know that sass-embedded
ships native pre-compiled versions of the gem for android architectures. So I can always fallback to running gem install sass-embedded
when I run my android app and it will work. But it will be nice to have it done during build step so as to have keep everything in the bootstrap ready to go. I have all other required gems compiled and working.
And also, I've tried fixating the versions I include in the bundled_gems
file to like 1.77.4-aarch64-linux-android
instead of just 1.77.4
but that ends up skipping bundling the gem altogether. Right now even though the extensions are skipped, I do have it unpacked in the bootstrap at least.
You don't need the Rakefile
at the root of the project. That is just a helper file for local development. During the gem installation only ext/sass/Rakefile
is needed.
I had checked the release workflow to get the gist.
Nice, but well there's no cli.rb
or embedded_sass_pb.rb
by default, I only see the cli.rb
being set up by root level Rakefile
.
Actually, the build steps act like it is building the gem from source. It's not like a regular gem install sass-embedded
.
I'm assuming you're cross compiling? That would likely be where your issue is coming from. ext/sass/Rakefile
builds a few things:
Let's say build arch is the arch of the machine that builds, and target arch is android.
dart-sass
+ cli.rb
- this need to be the target arch.protoc.exe
+ embedded_sass_pb.rb
- this depends on dart-sass
+ cli.rb
to be the build arch, in order to run the cli and get the right version. - This is a conflict with step 2, and that's why the Rakefile
at the root actually runs ext/sass/Rakefile
twice for cross-compiling. You can use rake parameter overrides to force it to use a certain file without dealing with this architecture difference.I only see the
cli.rb
being set up by root level Rakefile.
The install
(default) target in ext/sass/Rakefile
will set it up if not found: https://github.com/sass-contrib/sass-embedded-host-ruby/blob/b33d2795e72c72bb40cf14d94c2e64ac5fb108b4/ext/sass/Rakefile#L7-L9
Yes it is a cross compilation using Android NDK in a termux-packages build system. I build ruby from a linux container for 4 target android arches.
Could you please describe the build steps for a normal system. Maybe I could try adapt rbinstall.rb or other steps for it it in my set up.
Here is the general steps this gem builds:
ext/sass/dart-sass
for current RbConfig::CONFIG['host_os']
and RbConfig::CONFIG['host_cpu']
, and generate ext/sass/cli.rb
.embedded_sass_pb.rb
does not exists:
./dart-sass/sass --embedded --version
to get protocolVersion
.embedded_sass.proto
for current protocolVersion
.protoc.exe
for current RbConfig::CONFIG['host_os']
and RbConfig::CONFIG['host_cpu']
.protoc.exe
to generate embedded_sass_pb.rb
.ext/sass/dart-sass
and ext/sass/cli.rb
.A few notes:
embedded_sass_pb.rb
pre-bundled.Rakefile
at the project root is a mainly just a helper to make running step 3 easy.So for your purpose, I think what you can do is:
embedded_sass_pb.rb
, then remove ext/sass/dart-sass
and ext/sass/cli.rb
.cli.rb
with architecture override, which will download the dart-sass
.In other words, try prepare the git repo into the state at step 3.i
, then when installing you only need step 3.ii
Ok. I think it's going to be very difficult to build the extension with the rbinstall.rb
's build-ext mechanism. My recommendation is to prepare the repo into a state that is ready for the architecture you're targeting, and then just use rbinstall.rb
to bundle it.
For example, save the follow script as prepare.rb
:
require 'bundler'
platform = ARGV[0]
system(*%w[git checkout HEAD -- sass-embedded.gemspec])
system(*%w[bundle])
system(*%w[bundle exec rake compile], "ext_platform=#{platform}")
ENV['gem_platform'] = platform
spec = Bundler.load_gemspec_uncached('sass-embedded.gemspec')
spec.platform = 'ruby'
File.write('sass-embedded.gemspec', spec.to_ruby)
Then, you can run ruby prepare.rb aarch64-linux-android
, and this will prepare the repo to be in already compiled and ready to use state for aarch64-linux-android
. Since you have a patching step, you can use the patch step to do this.
Normally, cross-compile relies on the cross-compile toolchain to target the right architecture. However, this gem is an exception that it does not use any compiler toolchain, thus from Rakefile
's point of view it has no way to properly know what cross-compile target you're running for. Therefore, I think the workaround above might be the best you can do.
Hey, this was really helpful! I understand the procedure a lot better now. I tried to adapt it to my set up. For example adding an adapted version of the root level Rakefile
and applying it's steps using something like:
if spec.name == "sass-embedded"
output = system("cd #{srcdir}/.bundle/gems/#{gem_name} && gem install google-protobuf && rake compile ext_platform=#{RbConfig::CONFIG['platform']}")
puts output
end
I was able to see logs and confirm the required target arch files were being prepared as you mentioned. The build succeeded.
But I kept getting this error during runtime on the app that certain files were still still from the host not the target arch:
:/data/data/sh.gourav.jekyllex/files/home $ irb
Ignoring sass-embedded-1.77.4 because its extensions are not built. Try: gem pristine sass-embedded --version 1.77.4
irb(main):001> require 'sass-embedded'
=> true
irb(main):002> Sass.compile_string('')
/data/data/sh.gourav.jekyllex/files/usr/lib/ruby/gems/3.3.0/gems/sass-embedded-1.77.4-x86_64-linux-gnu/lib/sass/compiler/connection.rb:58: warning: error: "/data/app/~~403zZg0feGSMNUJRtmSEvA==/sh.gourav.jekyllex-kuFpzrgCodwdC3hznV-OUQ==/lib/arm64/lib1835.so" is for EM_X86_64 (62) instead of EM_AARCH64 (183)
/data/data/sh.gourav.jekyllex/files/usr/lib/ruby/gems/3.3.0/gems/sass-embedded-1.77.4-x86_64-linux-gnu/lib/sass/compiler/varint.rb:20:in `readbyte': end of file reached (EOFError)
from /data/data/sh.gourav.jekyllex/files/usr/lib/ruby/gems/3.3.0/gems/sass-embedded-1.77.4-x86_64-linux-gnu/lib/sass/compiler/varint.rb:20:in `block in read'
from <internal:kernel>:187:in `loop'
from /data/data/sh.gourav.jekyllex/files/usr/lib/ruby/gems/3.3.0/gems/sass-embedded-1.77.4-x86_64-linux-gnu/lib/sass/compiler/varint.rb:19:in `read'
from /data/data/sh.gourav.jekyllex/files/usr/lib/ruby/gems/3.3.0/gems/sass-embedded-1.77.4-x86_64-linux-gnu/lib/sass/compiler/connection.rb:43:in `block (2 levels) in listen'
from <internal:kernel>:187:in `loop'
from /data/data/sh.gourav.jekyllex/files/usr/lib/ruby/gems/3.3.0/gems/sass-embedded-1.77.4-x86_64-linux-gnu/lib/sass/compiler/connection.rb:42:in `block in listen'
irb(main):003>
It turns out that it was the best for me to somehow install the platform native gem directly from rubygems. So I ended up adding this to rbinstall.rb
after the bundled-gems
installation steps. And things just work fine now.
system("gem install sass-embedded -v 1.77.4 --platform #{RbConfig::CONFIG['platform']} --ignore-dependencies -i #{install_dir}")
Maybe I'll try to implement it in graceful way in the future but I guess this is the best to get things rolling for now.
Hi!
Context: I am trying to create installable ruby bootstraps for android architectures. I have altered the default bundled gems so that I can customize what gems are pre-installed. I wish to include the gem
sass-embedded
as well.To do so I want to understand the build steps to set up this gem's native extensions.
During ruby's build,
rbinstall.rb
is responsible for building the a gem's extensions and then installing gems to the correct place. I patch it introduce a step to give preference from my build files as sometimes things need to be altered for android arch.Based on the extension being built for a gem, the installation is assigned a builder and the
:build_args
from the installation options are passed to it.builder.rb
and since it'll be
RakeBuilder
for this gem, upon inspection I see the args are passed to the rake command. So I can run tasks likecompile
by passing them to the:build_args
inoptions
.The catch here is that it assumes the Rakefile is always from the gem's extension(
ext
) folder. Now, from what I understand, the taskinstall
inext/sass/Rakefile
depends on taskcompile
fromRakefile
being pre-run to set up required files. Thus, I can't useoptions[:build_args] = ["compile"]
before running the steps from the top level Rakefile. Thus it skips the extensions but installs the other files. But then requiringsass-embedded
inirb
keeps saying that it will be ignored as it's extensions are not built.The possible fixes could be:
ext/sass
and maybe run something likerake compile install
so the tasks run from one place. Which I can then use in the args. I need some help to do that.Introduce additional steps in
rbinstall.rb
to run required tasks before proceeding with the normal installation.This is more hacky and might take time to work on as there are caveats. For example, the
rake
it should use should be the one from target OS's ruby build. It may be tricky to ensure because ruby's installation first requires a host build and then a target build.I hope I was able to describe the issue in detail. Any help would be appreciated!