1 { stdenv, buildPackages, lib
2 , fetchurl, fetchpatch, fetchFromSavannah
3 , zlib, gdbm, ncurses, readline, groff, libyaml, libffi, jemalloc, autoreconfHook, bison
4 , autoconf, libiconv, libobjc, libunwind, Foundation
5 , buildEnv, bundler, bundix, cargo, rustPlatform, rustc
6 , makeBinaryWrapper, buildRubyGem, defaultGemConfig, removeReferencesTo
8 , linuxPackages, libsystemtap
15 opString = lib.optionalString;
16 config = import ./config.nix { inherit fetchFromSavannah; };
17 rubygems = import ./rubygems { inherit stdenv lib fetchurl gitUpdater; };
19 # Contains the ruby version heuristics
20 rubyVersion = import ./ruby-version.nix { inherit lib; };
22 generic = { version, hash, cargoHash ? null }: let
24 atLeast31 = lib.versionAtLeast ver.majMin "3.1";
25 atLeast32 = lib.versionAtLeast ver.majMin "3.2";
26 # https://github.com/ruby/ruby/blob/v3_2_2/yjit.h#L21
27 yjitSupported = atLeast32 && (stdenv.hostPlatform.isx86_64 || (!stdenv.hostPlatform.isWindows && stdenv.hostPlatform.isAarch64));
28 rubyDrv = lib.makeOverridable (
29 { stdenv, buildPackages, lib
30 , fetchurl, fetchpatch, fetchFromSavannah
31 , rubygemsSupport ? true
32 , zlib, zlibSupport ? true
33 , openssl, opensslSupport ? true
34 , gdbm, gdbmSupport ? true
35 , ncurses, readline, cursesSupport ? true
36 , groff, docSupport ? true
37 , libyaml, yamlSupport ? true
38 , libffi, fiddleSupport ? true
39 , jemalloc, jemallocSupport ? false
40 , linuxPackages, systemtap ? linuxPackages.systemtap, libsystemtap, dtraceSupport ? false
41 # By default, ruby has 3 observed references to stdenv.cc:
44 # ruby -e "puts RbConfig::CONFIG['configure_args']"
46 # $out/${passthru.libPath}/${stdenv.hostPlatform.system}/rbconfig.rb
48 # $(nix-build -A ruby)/lib/ruby/2.6.0/x86_64-linux/rbconfig.rb
49 # - In $out/lib/libruby.so and/or $out/lib/libruby.dylib
50 , removeReferencesTo, jitSupport ? yjitSupport
51 , cargo, rustPlatform, rustc, yjitSupport ? yjitSupported
52 , autoreconfHook, bison, autoconf
53 , buildEnv, bundler, bundix
54 , libiconv, libobjc, libunwind, Foundation
55 , makeBinaryWrapper, buildRubyGem, defaultGemConfig
56 , baseRuby ? buildPackages.ruby.override {
58 rubygemsSupport = false;
60 , useBaseRuby ? stdenv.hostPlatform != stdenv.buildPlatform
63 stdenv.mkDerivation ( finalAttrs: {
68 url = "https://cache.ruby-lang.org/pub/ruby/${ver.majMin}/ruby-${ver}.tar.gz";
72 # Have `configure' avoid `/usr/bin/nroff' in non-chroot builds.
73 NROFF = if docSupport then "${groff}/bin/nroff" else null;
75 outputs = [ "out" ] ++ lib.optional docSupport "devdoc";
79 nativeBuildInputs = [ autoreconfHook bison removeReferencesTo ]
80 ++ (op docSupport groff)
81 ++ (ops (dtraceSupport && stdenv.hostPlatform.isLinux) [ systemtap libsystemtap ])
82 ++ ops yjitSupport [ rustPlatform.cargoSetupHook cargo rustc ]
83 ++ op useBaseRuby baseRuby;
84 buildInputs = [ autoconf ]
85 ++ (op fiddleSupport libffi)
86 ++ (ops cursesSupport [ ncurses readline ])
87 ++ (op zlibSupport zlib)
88 ++ (op opensslSupport openssl)
89 ++ (op gdbmSupport gdbm)
90 ++ (op yamlSupport libyaml)
91 # Looks like ruby fails to build on darwin without readline even if curses
92 # support is not enabled, so add readline to the build inputs if curses
93 # support is disabled (if it's enabled, we already have it) and we're
95 ++ op (!cursesSupport && stdenv.hostPlatform.isDarwin) readline
96 ++ ops stdenv.hostPlatform.isDarwin [ libiconv libobjc libunwind Foundation ];
97 propagatedBuildInputs = op jemallocSupport jemalloc;
99 enableParallelBuilding = true;
100 # /build/ruby-2.7.7/lib/fileutils.rb:882:in `chmod':
101 # No such file or directory @ apply2files - ...-ruby-2.7.7-devdoc/share/ri/2.7.0/system/ARGF/inspect-i.ri (Errno::ENOENT)
102 # make: *** [uncommon.mk:373: do-install-all] Error 1
103 enableParallelInstalling = false;
105 patches = op (lib.versionOlder ver.majMin "3.1") ./do-not-regenerate-revision.h.patch
107 if atLeast32 then ./do-not-update-gems-baseruby-3.2.patch
108 else ./do-not-update-gems-baseruby.patch
110 ++ ops (ver.majMin == "3.0") [
111 # Ruby 3.0 adds `-fdeclspec` to $CC instead of $CFLAGS. Fixed in later versions.
113 url = "https://github.com/ruby/ruby/commit/0acc05caf7518cd0d63ab02bfa036455add02346.patch";
114 hash = "sha256-43hI9L6bXfeujgmgKFVmiWhg7OXvshPCCtQ4TxqK1zk=";
118 # When using a baseruby, ruby always sets "libdir" to the build
119 # directory, which nix rejects due to a reference in to /build/ in
120 # the final product. Removing this reference doesn't seem to break
121 # anything and fixes cross compliation.
122 ./dont-refer-to-build-dir.patch
125 cargoRoot = opString yjitSupport "yjit";
127 cargoDeps = if yjitSupport then rustPlatform.fetchCargoTarball {
128 inherit (finalAttrs) src;
129 sourceRoot = "${finalAttrs.pname}-${version}/${finalAttrs.cargoRoot}";
130 hash = assert cargoHash != null; cargoHash;
133 postUnpack = opString rubygemsSupport ''
134 rm -rf $sourceRoot/{lib,test}/rubygems*
135 cp -r ${rubygems}/lib/rubygems* $sourceRoot/lib
139 sed -i configure.ac -e '/config.guess/d'
140 cp --remove-destination ${config}/config.guess tool/
141 cp --remove-destination ${config}/config.sub tool/
145 (lib.enableFeature (!stdenv.hostPlatform.isStatic) "shared")
146 (lib.enableFeature true "pthread")
147 (lib.withFeatureAs true "soname" "ruby-${version}")
148 (lib.withFeatureAs useBaseRuby "baseruby" "${baseRuby}/bin/ruby")
149 (lib.enableFeature dtraceSupport "dtrace")
150 (lib.enableFeature jitSupport "jit-support")
151 (lib.enableFeature yjitSupport "yjit")
152 (lib.enableFeature docSupport "install-doc")
153 (lib.withFeature jemallocSupport "jemalloc")
154 (lib.withFeatureAs docSupport "ridir" "${placeholder "devdoc"}/share/ri")
155 # ruby enables -O3 for gcc, however our compiler hardening wrapper
156 # overrides that by enabling `-O2` which is the minimum optimization
157 # needed for `_FORTIFY_SOURCE`.
158 ] ++ lib.optional stdenv.cc.isGNU "CFLAGS=-O3" ++ [
159 ] ++ ops stdenv.hostPlatform.isDarwin [
160 # on darwin, we have /usr/include/tk.h -- so the configure script detects
161 # that tk is installed
163 # on yosemite, "generating encdb.h" will hang for a very long time without this flag
164 "--with-setjmp-type=setjmp"
165 ] ++ ops stdenv.hostPlatform.isFreeBSD [
166 "rb_cv_gnu_qsort_r=no"
167 "rb_cv_bsd_qsort_r=yes"
170 preConfigure = opString docSupport ''
171 # rdoc creates XDG_DATA_DIR (defaulting to $HOME/.local/share) even if
172 # it's not going to be used.
176 # fails with "16993 tests, 2229489 assertions, 105 failures, 14 errors, 89 skips"
177 # mostly TZ- and patch-related tests
178 # TZ- failures are caused by nix sandboxing, I didn't investigate others
182 # Ruby installs gems here itself now.
183 mkdir -pv "$out/${finalAttrs.passthru.gemPath}"
184 export GEM_HOME="$out/${finalAttrs.passthru.gemPath}"
187 installFlags = lib.optional docSupport "install-doc";
188 # Bundler tries to create this directory
190 rbConfig=$(find $out/lib/ruby -name rbconfig.rb)
191 # Remove references to the build environment from the closure
192 sed -i '/^ CONFIG\["\(BASERUBY\|SHELL\|GREP\|EGREP\|MKDIR_P\|MAKEDIRS\|INSTALL\)"\]/d' $rbConfig
193 # Remove unnecessary groff reference from runtime closure, since it's big
194 sed -i '/NROFF/d' $rbConfig
196 lib.optionalString (!jitSupport) ''
197 # Get rid of the CC runtime dependency
198 remove-references-to \
201 remove-references-to \
204 sed -i '/CC_VERSION_MESSAGE/d' $rbConfig
208 # Allow to override compiler. This is important for cross compiling as
209 # we need to set a compiler that is different from the build one.
211 -e 's/CONFIG\["CC"\] = "\(.*\)"/CONFIG["CC"] = if ENV["CC"].nil? || ENV["CC"].empty? then "\1" else ENV["CC"] end/' \
212 -e 's/CONFIG\["CXX"\] = "\(.*\)"/CONFIG["CXX"] = if ENV["CXX"].nil? || ENV["CXX"].empty? then "\1" else ENV["CXX"] end/'
214 # Remove unnecessary external intermediate files created by gems
215 extMakefiles=$(find $out/${finalAttrs.passthru.gemPath} -name Makefile)
216 for makefile in $extMakefiles; do
217 make -C "$(dirname "$makefile")" distclean
219 find "$out/${finalAttrs.passthru.gemPath}" \( -name gem_make.out -o -name mkmf.log -o -name exts.mk \) -delete
220 # Bundler tries to create this directory
221 mkdir -p $out/nix-support
222 cat > $out/nix-support/setup-hook <<EOF
224 addToSearchPath GEM_PATH \$1/${finalAttrs.passthru.gemPath}
227 addToSearchPath RUBYLIB \$1/lib/ruby/site_ruby
228 addToSearchPath RUBYLIB \$1/lib/ruby/site_ruby/${ver.libDir}
229 addToSearchPath RUBYLIB \$1/lib/ruby/site_ruby/${ver.libDir}/${stdenv.hostPlatform.system}
232 addEnvHooks "$hostOffset" addGemPath
233 addEnvHooks "$hostOffset" addRubyLibPath
235 '' + opString docSupport ''
236 # Prevent the docs from being included in the closure
237 sed -i "s|\$(DESTDIR)$devdoc|\$(datarootdir)/\$(RI_BASE_NAME)|" $rbConfig
238 sed -i "s|'--with-ridir=$devdoc/share/ri'||" $rbConfig
240 # Add rbconfig shim so ri can find docs
241 mkdir -p $devdoc/lib/ruby/site_ruby
242 cp ${./rbconfig.rb} $devdoc/lib/ruby/site_ruby/rbconfig.rb
243 '' + opString useBaseRuby ''
244 # Prevent the baseruby from being included in the closure.
245 remove-references-to \
247 $rbConfig $out/lib/libruby*
250 installCheckPhase = ''
251 overriden_cc=$(CC=foo $out/bin/ruby -rrbconfig -e 'puts RbConfig::CONFIG["CC"]')
252 if [[ "$overriden_cc" != "foo" ]]; then
253 echo "CC cannot be overwritten: $overriden_cc != foo" >&2
257 fallback_cc=$(unset CC; $out/bin/ruby -rrbconfig -e 'puts RbConfig::CONFIG["CC"]')
258 if [[ "$fallback_cc" != "$CC" ]]; then
259 echo "CC='$fallback_cc' should be '$CC' by default" >&2
263 doInstallCheck = true;
265 disallowedRequisites = op (!jitSupport) stdenv.cc
266 ++ op useBaseRuby baseRuby;
269 description = "Object-oriented language for quick and easy programming";
270 homepage = "https://www.ruby-lang.org/";
271 license = licenses.ruby;
272 maintainers = with maintainers; [ manveru ];
273 platforms = platforms.all;
274 mainProgram = "ruby";
275 knownVulnerabilities = op (lib.versionOlder ver.majMin "3.0") "This Ruby release has reached its end of life. See https://www.ruby-lang.org/en/downloads/branches/.";
281 libPath = "lib/${rubyEngine}/${ver.libDir}";
282 gemPath = "lib/${rubyEngine}/gems/${ver.libDir}";
283 devEnv = import ./dev.nix {
284 inherit buildEnv bundler bundix;
285 ruby = finalAttrs.finalPackage;
289 inherit (import ../../ruby-modules/with-packages {
290 inherit lib stdenv makeBinaryWrapper buildRubyGem buildEnv;
291 gemConfig = defaultGemConfig;
292 ruby = finalAttrs.finalPackage;
293 }) withPackages buildGems gems;
294 } // lib.optionalAttrs useBaseRuby {
301 mkRubyVersion = rubyVersion;
305 version = rubyVersion "3" "1" "6" "";
306 hash = "sha256-DQ2vuFnnZ2NDJXGjEJ0VN9l2JmvjCDRFZR3Gje7SXCI=";
310 version = rubyVersion "3" "2" "5" "";
311 hash = "sha256-7wYQtJj2D7XP13tRrbPBD0yo7ZoXy4fGHlvqMUrDShY=";
312 cargoHash = "sha256-6du7RJo0DH+eYMOoh3L31F3aqfR5+iG1iKauSV1uNcQ=";
316 version = rubyVersion "3" "3" "5" "";
317 hash = "sha256-N4GjUEIiwvJstLnrnBoS2/SUTTZs4kqf+M+Z7LznUZY=";
318 cargoHash = "sha256-GeelTMRFIyvz1QS2L+Q3KAnyQy7jc0ejhx3TdEFVEbk=";
322 version = rubyVersion "3" "4" "0" "preview2";
323 hash = "sha256-RDzX7FSt5HhryXTOn11J8XKmD47chLWXt/4r0qlLg3E=";
324 cargoHash = "sha256-kdfNY8wVmSRR+cwEDYge/HDPRvdTNKLk/BhgqQeelOg=";