├── .gitignore ├── LICENSE ├── README.md ├── bin ├── rakudobrew └── rakudobrew.bat └── lib ├── File └── Which.pm └── Rakudobrew ├── Build.pm ├── ShellHook.pm ├── ShellHook ├── Bash.pm ├── Fish.pm ├── Sh.pm └── Zsh.pm ├── Tools.pm ├── Variables.pm └── VersionHandling.pm /.gitignore: -------------------------------------------------------------------------------- 1 | CURRENT 2 | MODE 3 | versions/* 4 | shims/* 5 | git_reference/ 6 | 7 | *.swp 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017 Tadeusz Sośnierz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATION NOTICE 2 | 3 | Rakudobrew has been renamed to Rakubrew. Head over to [rakubrew.org](https://rakubrew.org/) 4 | for the current version. 5 | 6 | # rakudobrew 7 | 8 | Rakudobrew helps to build one or more versions of Rakudo and quickly switch between them. 9 | It's a perlbrew and plenv look alike and supports both flavours of commands. 10 | 11 | Rakudobrew can work by modifying $PATH in place (which is a more down to the metal) as well 12 | as with shims (which enables advanced features, such as local versions). 13 | 14 | ## Installation 15 | 16 | - On \*nix do: 17 | ``` 18 | git clone https://github.com/tadzik/rakudobrew ~/.rakudobrew 19 | export PATH=~/.rakudobrew/bin:$PATH 20 | # or fish shell: set -U fish_user_paths ~/.rakudobrew/bin/ $fish_user_paths 21 | rakudobrew init # Instructions for permanent installation. 22 | ``` 23 | 24 | - On Windows CMD do: 25 | ``` 26 | git clone https://github.com/tadzik/rakudobrew %USERPROFILE%\rakudobrew 27 | SET PATH=%USERPROFILE%\rakudobrew\bin;%PATH% 28 | rakudobrew init # Instructions for permanent installation. 29 | ``` 30 | 31 | - On Windows PowerShell do: 32 | ``` 33 | git clone https://github.com/tadzik/rakudobrew $Env:USERPROFILE\rakudobrew 34 | $Env:PATH = "$Env:USERPROFILE\rakudobrew\bin;$Env:PATH" 35 | rakudobrew init # Instructions for permanent installation. 36 | ``` 37 | 38 | ## Windows notes 39 | 40 | Rakudobrew requires Perl 5 and Git to be installed. You can download and install these from 41 | * http://strawberryperl.com/ 42 | * https://www.git-scm.com/downloads 43 | 44 | If you want to use the Microsoft compiler `cl`, you have to make sure the compiler is on 45 | your `PATH` and you have the environment variables `cl` requires set. 46 | This happens automatically when using the *Native Tools Command Prompt* but has to be done 47 | manually when using a normal terminal (or PowerShell). The script `vcvars32.bat` (which is in the same 48 | folder as `cl`) can set these variables up automatically for you. 49 | 50 | On PowerShell this requires 51 | some additional trickery as described on StackOverflow: 52 | 53 | It might be necessary to use an Administrative console to work 54 | around a problem with permissions that go wrong during the build process. 55 | 56 | ## Bootstrapping a Perl 6 implementation 57 | 58 | - Run something like: 59 | 60 | ``` 61 | $ rakudobrew build moar 62 | ``` 63 | 64 | to build the latest [Rakudo](https://github.com/rakudo/rakudo) release 65 | (in this case, on the [MoarVM](https://github.com/MoarVM/MoarVM) backend). 66 | 67 | - Once that's build switch to it (substitute the version rakudobrew just built): 68 | 69 | ``` 70 | $ rakudobrew switch moar-2019.03.1 71 | ``` 72 | 73 | - To install [zef](https://github.com/ugexe/zef) (the Perl 6 module manager), do: 74 | 75 | 76 | ``` 77 | $ rakudobrew build-zef 78 | ``` 79 | 80 | ## global vs shell vs local 81 | 82 | The `global` version is the one that is active when none of the overrides of `shell` 83 | and `local` are triggered. 84 | 85 | The `shell` version changes the active Rakudo version just in the current shell. 86 | Closing the current shell also looses the `shell` version. 87 | 88 | The `local` version is specific to a folder. When CWD is in that folder or a sub folder 89 | that version of Rakudo is used. Only works in `shim` mode. To unset a local version 90 | one must delete the `.PL6ENV_VERSION` file in the respective folder. 91 | 92 | ## Modes 93 | 94 | Rakudo brew has two modes of operation: `env` and `shim`. 95 | 96 | In `env` mode rakudobrew modifies the `$PATH` variable as needed when switching between 97 | versions. This is neat because one then runs the executables directly. This is the default mode 98 | on \*nix. 99 | 100 | In `shim` mode rakudobrew generates wrapper scripts called shims for all 101 | executables it can find in all the different Rakudo installations. These 102 | shims forward to the actual executable when called. This mechanism allows for 103 | some advanced features, such as local versions. When installing a module that 104 | adds scripts one must make rakudobrew aware of these new scripts. This is done 105 | with 106 | 107 | ``` 108 | $ rakudobrew rehash 109 | ``` 110 | In `env` mode this is not necessary. 111 | 112 | ## Registering external versions 113 | 114 | To add a Rakudo installation to rakudobrew that was created without rakudobrew 115 | one should do: 116 | 117 | ``` 118 | $ rakudobrew register name-of-version /path/to/rakudo/install/directory 119 | ``` 120 | 121 | ## Upgrading your Perl 6 implementation 122 | 123 | ``` 124 | $ rakudobrew build moar 125 | ``` 126 | 127 | ## Upgrading rakudobrew itself 128 | 129 | ``` 130 | $ rakudobrew self-upgrade 131 | ``` 132 | 133 | ## Uninstall rakudobrew and its Perl 6(s) 134 | 135 | To remove rakudobrew and any Perl 6 implementations it's installed on your system, 136 | just remove or rename the `~/.rakudobrew` directory. 137 | 138 | ## Specifying custom git path 139 | 140 | In case git is not in any standard `PATH` on your system, you can specify a custom path 141 | to the git binary using a `GIT_BINARY` environment variable: 142 | 143 | ``` 144 | $ GIT_BINARY="%USERPROFILE%\Local Settings\Application Data\GitHub\PORTAB~1\bin\git.exe" rakudobrew build all 145 | ``` 146 | 147 | ## Specifying a git protocol 148 | 149 | By default, rakudobrew will use the git protocol when it clones repositories. 150 | To override this setting, use the `GIT_PROTOCOL` environment variable. 151 | 152 | ``` 153 | $ GIT_PROTOCOL=ssh rakudobrew list-available 154 | # uses git@github.com:/rakudo/rakudo.git 155 | 156 | $ GIT_PROTOCOL=https rakudobrew list-available 157 | # uses https://github.com/rakudo/rakudo.git 158 | ``` 159 | 160 | ## Command-line switches 161 | 162 | ### `version` or `current` 163 | Show the currently active Rakudo version. 164 | 165 | ### `versions` or `list` 166 | List all installed Rakudo installations. 167 | 168 | ### `global [version]` or `switch [version]` 169 | Show or set the globally configured Rakudo version. 170 | 171 | ### `shell [--unset|version]` 172 | Show, set or unset the shell version. 173 | 174 | ### `local [version] 175 | Show or set the local version. 176 | 177 | ### `nuke [version]` or `unregister [version]` 178 | Removes an installed or registered version. Versions built by rakudobrew are 179 | actually deleted, registered versions are only unregistered but not deleted. 180 | 181 | ### `rehash` 182 | Regenerate all shims. Newly installed scripts will not work unless this is 183 | called. This is only necessary in `shim` mode. 184 | 185 | ### `list-available` 186 | List all Rakudo versions that can be installed. 187 | 188 | ### `build [jvm|moar|moar-blead|all] [tag|branch|sha-1] [--configure-opts=]` 189 | Build a Rakudo version. The arguments are: 190 | - The backend. 191 | - `moar-blead` is the moar and nqp backends at the master branch. 192 | - `all` will build all backends. 193 | - The version to build. Call `list-available` to see a list of available 194 | versions. When left empty the latest release is built. 195 | It is also possible to specify a commit sha or branch to build. 196 | - Configure options. 197 | 198 | ### `triple [rakudo-ver [nqp-ver [moar-ver]]]` 199 | Build a specific set of Rakudo, NQP and MoarVM commits. 200 | 201 | ### `register ` 202 | Register an externaly built / installed Rakudo version with Rakudobrew. 203 | 204 | ### `build-zef` 205 | Install Zef into the current Rakudo version. 206 | 207 | ### `exec [command-args]` 208 | Explicitly call an executable. You normally shouldn't need to do this. 209 | 210 | ### `rakudobrew which ` 211 | Show the full path to the executable. 212 | 213 | ### `whence [--path] ` 214 | List all versions that contain the given command. when `--path` is given the 215 | path of the executables is given instead. 216 | 217 | ### `mode [env|shim]` 218 | Show or set the mode of operation. 219 | 220 | ### `self-upgrade` 221 | Upgrade Rakudobrew itself. 222 | 223 | ### `init` 224 | Show installation instructions. 225 | 226 | ### `test [version|all]` 227 | Run Rakudo tests in the current or given version. 228 | 229 | -------------------------------------------------------------------------------- /bin/rakudobrew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # I AM RAKUDOBREW. ALSO POSSIBLY SPARTACUS. OUTLOOK CLOUDY, ASK AGAIN LATER. 3 | use strict; 4 | use warnings; 5 | use 5.010; 6 | use FindBin qw($RealBin $RealScript); 7 | use lib "$RealBin/../lib"; 8 | use File::Path qw(remove_tree); 9 | use File::Spec::Functions qw(catfile catdir splitpath updir); 10 | 11 | use Rakudobrew::Variables; 12 | use Rakudobrew::Tools; 13 | use Rakudobrew::VersionHandling; 14 | use Rakudobrew::Build; 15 | 16 | # Detect incompatible version upgrade and notify user of the breakage. 17 | { 18 | my $backends = join '|', Rakudobrew::Build::available_backends(); 19 | opendir(my $dh, $prefix); 20 | my $old_version_found = grep { /^($backends)/ } readdir($dh); 21 | closedir $dh; 22 | 23 | if ($old_version_found) { 24 | say STDERR <<"EOS"; 25 | You seem to have upgraded rakudobrew to a newer version not compatible with 26 | your current directory layout. 27 | 28 | To use the new version you need to completely remove rakudobrew by deleting 29 | $prefix and installing again. See 30 | 31 | https://github.com/tadzik/rakudobrew 32 | 33 | for installation instructions. You will also need to change the rakudobrew 34 | entry in your shell startup file (~/.profile) a bit. Run `rakudobrew init` 35 | again to see how. 36 | 37 | If you don't want to upgrade, but just continue using the old version, 38 | do the following: 39 | 40 | cd $prefix && git checkout v1 41 | EOS 42 | exit 1; 43 | } 44 | } 45 | 46 | unless (-d $shim_dir) { 47 | mkdir $shim_dir; 48 | } 49 | unless (-d $versions_dir) { 50 | mkdir $versions_dir; 51 | } 52 | unless (-d $git_reference) { 53 | mkdir $git_reference; 54 | } 55 | 56 | { # Check whether we are called as a shim and forward if yes. 57 | my (undef, undef, $prog_name) = splitpath($0); 58 | 59 | # TODO: Mac is also case insensitive. Is this way to compensate for insensitivity safe? 60 | if ($prog_name ne $brew_name && 61 | ($^O !~ /win32/i || $prog_name =~ /^\Q$brew_name\E\z/i)) { 62 | do_exec($prog_name, \@ARGV); 63 | } 64 | } 65 | 66 | if (@ARGV && $ARGV[0] eq 'internal_hooked') { # The hook is there, all good! 67 | shift; # Remove the hook so processing code below doesn't need to care about it. 68 | } 69 | elsif (@ARGV && $ARGV[0] =~ /^internal_/ # It's an internal_ method, all good! 70 | || !@ARGV || $ARGV[0] eq 'init' # We don't want to annoy the user with missing 71 | # hook messages if she might not have even completed 72 | # the installation process. 73 | || $^O =~ /win32/i ) # Windows doesn't support shell hooks. So don't whine about it here. 74 | {} 75 | elsif (get_brew_mode() eq 'env' || @ARGV && $ARGV[0] eq 'shell' || @ARGV >= 2 && $ARGV[0] eq 'mode' && $ARGV[1] eq 'env') { 76 | say STDERR "The shell hook required to use rakudobrew in 'env' mode and use the 'shell' command seems not to be installed."; 77 | say STDERR "Run '$brew_name init' for installation instructions."; 78 | exit 1; 79 | } 80 | 81 | 82 | my $arg = shift // 'help'; 83 | 84 | if ($arg eq 'version' || $arg eq 'current') { 85 | if (my $c = get_version()) { 86 | say "Currently running $c" 87 | } else { 88 | say STDERR "Not running anything at the moment. Use '$brew_name switch' to set a version"; 89 | exit 1; 90 | } 91 | 92 | } elsif ($arg eq 'versions' || $arg eq 'list') { 93 | my $cur = get_version() // ''; 94 | map { 95 | my $version_line = ''; 96 | $version_line .= $_ eq $cur ? '* ' : ' '; 97 | $version_line .= $_; 98 | $version_line .= ' -> ' . get_version_path($_) if is_registered_version($_); 99 | say $version_line; 100 | } get_versions(); 101 | 102 | } elsif ($arg eq 'global' || $arg eq 'switch') { 103 | if (!@ARGV) { 104 | my $version = get_global_version(); 105 | if ($version) { 106 | say $version; 107 | } 108 | else { 109 | say "$brew_name: no global version configured"; 110 | } 111 | } 112 | else { 113 | match_and_run($ARGV[0], sub { 114 | set_global_version(shift); 115 | }); 116 | } 117 | 118 | } elsif ($arg eq 'shell') { 119 | if ($^O =~ /win32/i) { 120 | say < rehash. 213 | rehash(); 214 | say "Done, built zef for $version"; 215 | } elsif (!exists $impls{$impl}) { 216 | my $warning = "Cannot build Rakudo with backend '$impl': this backend "; 217 | if ($impl eq "parrot") { 218 | $warning .= "is no longer supported."; 219 | } else { 220 | $warning .= "does not exist."; 221 | } 222 | say $warning; 223 | exit 1; 224 | } 225 | else { 226 | my $configure_opts = ''; 227 | if (@ARGV && $ARGV[0] =~ /^--configure-opts=/) { 228 | $configure_opts = shift; 229 | $configure_opts =~ s/^\-\-configure-opts=//; 230 | $configure_opts =~ s/^'//; 231 | $configure_opts =~ s/'$//; 232 | } 233 | 234 | my $name = "$impl-$ver"; 235 | $name = $impl if $impl eq 'moar-blead' && $ver eq 'master'; 236 | 237 | if ($impl && $impl eq 'all') { 238 | for (Rakudobrew::Build::available_backends()) { 239 | Rakudobrew::Build::build_impl($_, $ver, $configure_opts); 240 | } 241 | } else { 242 | Rakudobrew::Build::build_impl($impl, $ver, $configure_opts); 243 | } 244 | 245 | # Might have new executables now -> rehash. 246 | rehash(); 247 | unless (get_version()) { 248 | set_global_version($name); 249 | } 250 | say "Done, $name built"; 251 | } 252 | 253 | } elsif ($arg eq 'triple') { 254 | my ($rakudo_ver, $nqp_ver, $moar_ver) = (shift, shift, shift); 255 | my $name = Rakudobrew::Build::build_triple($rakudo_ver, $nqp_ver, $moar_ver); 256 | 257 | # Might have new executables now -> rehash 258 | rehash(); 259 | unless (get_version()) { 260 | set_global_version($name); 261 | } 262 | say "Done, $name built"; 263 | 264 | } elsif ($arg eq 'register') { 265 | my ($name, $path) = (shift, shift); 266 | if (!$name || !$path) { 267 | say STDERR "$brew_name: Need a version name and rakudo installation path"; 268 | exit 1; 269 | } 270 | if (version_exists($name)) { 271 | say STDERR "$brew_name: Version $name already exists"; 272 | exit 1; 273 | } 274 | 275 | sub invalid() { 276 | say STDERR "$brew_name: No valid rakudo installation found at '$path'"; 277 | exit 1; 278 | } 279 | invalid() if !-d $path; 280 | $path = catdir($path, 'install') if !-f catfile($path, 'bin', 'perl6'); 281 | invalid() if !-f catdir($path, 'bin', 'perl6'); 282 | 283 | spurt(catfile($versions_dir, $name), $path); 284 | 285 | } elsif ($arg eq 'build-zef') { 286 | my $version = get_version(); 287 | if (!$version) { 288 | say STDERR "$brew_name: No version set."; 289 | exit 1; 290 | } 291 | Rakudobrew::Build::build_zef($version); 292 | # Might have new executables now -> rehash 293 | rehash(); 294 | say "Done, built zef for $version"; 295 | 296 | } elsif ($arg eq 'build-panda') { 297 | say "panda is discontinued; please use zef (rakudobrew build-zef) instead"; 298 | 299 | } elsif ($arg eq 'exec') { 300 | my $prog_name = shift; 301 | do_exec($prog_name, \@ARGV); 302 | 303 | } elsif ($arg eq 'which') { 304 | if (!@ARGV) { 305 | say STDERR "Usage: $brew_name which "; 306 | } 307 | else { 308 | my $version = get_version(); 309 | if (!$version) { 310 | say STDERR "$brew_name: No version set."; 311 | exit 1; 312 | } 313 | map {say $_} which($ARGV[0], $version); 314 | } 315 | 316 | } elsif ($arg eq 'whence') { 317 | if (!@ARGV) { 318 | say STDERR "Usage: $brew_name whence [--path] "; 319 | } 320 | else { 321 | my $param = shift; 322 | my $pathmode = $param eq '--path'; 323 | my $prog = $pathmode ? shift : $param; 324 | map {say $_} whence($prog, $pathmode); 325 | } 326 | 327 | } elsif ($arg eq 'mode') { 328 | if (!@ARGV) { 329 | say get_brew_mode(); 330 | } 331 | else { 332 | set_brew_mode($ARGV[0]); 333 | } 334 | 335 | } elsif ($arg eq 'self-upgrade') { 336 | self_upgrade(); 337 | 338 | } elsif ($arg eq 'init') { 339 | init(@ARGV); 340 | 341 | } elsif ($arg eq 'test') { 342 | my $version = shift; 343 | if ($version && $version eq 'all') { 344 | for (get_versions()) { 345 | test($_); 346 | } 347 | } else { 348 | test($version); 349 | } 350 | } elsif ($arg eq 'internal_shell_hook') { 351 | no strict 'refs'; 352 | my $shell = shift; 353 | my $sub = shift; 354 | 355 | eval "require Rakudobrew::ShellHook::$shell"; 356 | "Rakudobrew::ShellHook::${shell}::$sub"->(@ARGV); 357 | 358 | } elsif ($arg eq 'internal_win_run') { 359 | my $prog_name = shift; 360 | my $path = which($prog_name, get_version()); 361 | # Do some filetype detection: 362 | # - .exe/.bat/.cmd -> return "filename" 363 | # - .nqp -> return "nqp filename" 364 | # - shebang line contains perl6 -> return "perl6 filename" 365 | # - shebang line contains perl -> return "perl filename" 366 | # - nothing of the above -> return "filename" # if we can't 367 | # figure out what to do with this 368 | # filename, let Windows have a try. 369 | # The first line is potentially the shebang. Thus the search for "perl" and/or perl6. 370 | my ($basename, undef, $suffix) = my_fileparse($prog_name); 371 | if($suffix =~ /^\Q\.(exe|bat|cmd)\E\z/i) { 372 | say $path; 373 | } 374 | elsif($suffix =~ /^\Q\.nqp\E\z/i) { 375 | say which('nqp', get_version()).' '.$path; 376 | } 377 | else { 378 | open(my $fh, '<', $path); 379 | my $first_line = <$fh>; 380 | close($fh); 381 | if($first_line =~ /#!.*perl6/) { 382 | say which('perl6', get_version()).' '.$path; 383 | } 384 | elsif($first_line =~ /#!.*perl/) { 385 | say 'perl '.$path; 386 | } 387 | else { 388 | say $path; 389 | } 390 | } 391 | 392 | } else { 393 | my $backends = join '|', Rakudobrew::Build::available_backends(), 'all'; 394 | say <<"EOT"; 395 | Usage: 396 | $brew_name version # or $brew_name current 397 | $brew_name versions # or $brew_name list 398 | $brew_name global [version] # or $brew_name switch [version] 399 | $brew_name shell [--unset|version] 400 | $brew_name local [version] 401 | $brew_name nuke [version] # or $brew_name unregister [version] 402 | $brew_name rehash 403 | 404 | $brew_name list-available 405 | $brew_name build [$backends] [tag|branch|sha-1] [--configure-opts=] 406 | $brew_name triple [rakudo-ver [nqp-ver [moar-ver]]] 407 | $brew_name register 408 | $brew_name build-zef 409 | 410 | $brew_name exec [command-args] 411 | $brew_name which 412 | $brew_name whence [--path] 413 | $brew_name mode [env|shim] 414 | $brew_name self-upgrade 415 | $brew_name init 416 | 417 | $brew_name test [version|all] 418 | EOT 419 | } 420 | 421 | exit; 422 | 423 | sub match_and_run { 424 | my ($version, $action) = @_; 425 | if (!$version) { 426 | say "Which version do you mean?"; 427 | say "Available builds:"; 428 | map {say} get_versions(); 429 | return; 430 | } 431 | opendir(my $dh, $versions_dir); 432 | if (grep { $_ eq $version } get_versions()) { 433 | $action->($version); 434 | } 435 | else { 436 | say "Sorry, '$version' not found."; 437 | my @match = grep { /\Q$version/ } get_versions(); 438 | if (@match) { 439 | say "Did you mean:"; 440 | say $_ for @match; 441 | } 442 | } 443 | } 444 | 445 | sub self_upgrade { 446 | chdir $prefix; 447 | run "$GIT pull"; 448 | } 449 | 450 | sub test { 451 | my $version = shift || get_version(); 452 | if (!$version) { 453 | say STDERR "$brew_name: No version set."; 454 | exit 1; 455 | } 456 | my @match = grep { /\Q$version/ } get_versions(); 457 | my ($matched, $ambiguous) = @match; 458 | if ($ambiguous) { 459 | my ($exact) = grep { $_ eq $version } @match; 460 | if ($exact) { 461 | ($matched, $ambiguous) = $exact; 462 | } 463 | } 464 | if ($matched and not $ambiguous) { 465 | say "Spectesting $matched"; 466 | chdir catdir($versions_dir, $matched); 467 | Rakudobrew::Build::make('spectest'); 468 | } elsif (@match) { 469 | say "Sorry, I'm not sure if you mean:"; 470 | say $_ for @match; 471 | } else { 472 | say "Sorry, I have no idea what '$version' is"; 473 | say "Have you run '$brew_name build $version' yet?"; 474 | } 475 | } 476 | 477 | sub nuke { 478 | my $version = shift; 479 | match_and_run($version, sub { 480 | my $matched = shift; 481 | if (is_registered_version($matched)) { 482 | say "Unregistering $matched"; 483 | unlink(catfile($versions_dir, $matched)); 484 | } 485 | elsif ($matched eq 'system') { 486 | say 'I refuse to nuke system Perl 6!'; 487 | exit 1; 488 | } 489 | else { 490 | say "Nuking $matched"; 491 | remove_tree(catdir($versions_dir, $matched)); 492 | } 493 | }); 494 | # Might have lost executables -> rehash 495 | rehash(); 496 | } 497 | 498 | sub init { 499 | my $brew_exec = catfile($RealBin, $brew_name); 500 | if ($^O =~ /win32/i) { 501 | say < right click on "Computer" -> Properties -> (Advanced system settings) 517 | # -> Advanced -> Environment Variables... -> System variables 518 | # -> select PATH -> Edit... -> prepend "$RealBin;$shim_dir;" 519 | # 520 | # WARNING: 521 | # Setting PATH to a string longer than 2048 chars (4096 on newer systems) can cause the 522 | # PATH to be truncated, your PATH being set to the empty string and only become available 523 | # again upon reboot and in the worst case cause your system to not boot anymore. 524 | # See https://web.archive.org/web/20190519191717/https://software.intel.com/en-us/articles/limitation-to-the-length-of-the-system-path-variable 525 | EOT 526 | } 527 | else { 528 | if (@_) { 529 | no strict 'refs'; 530 | my $shell = $_[0]; 531 | 532 | eval "require Rakudobrew::ShellHook::$shell"; 533 | 534 | if ($@) { 535 | say STDERR "Couldn't find shell hook implementation for shell '$shell'."; 536 | exit 1; 537 | } 538 | my $code = "Rakudobrew::ShellHook::${shell}::get_init_code"; 539 | say $code->(); 540 | } 541 | else { 542 | my @available_shell_hooks; 543 | opendir(my $dh, catdir($prefix, 'lib', 'Rakudobrew', 'ShellHook')) or die "$brew_name: lib dir not found"; 544 | while (my $entry = readdir $dh) { 545 | if ($entry =~ /(.*)\.pm$/) { 546 | push @available_shell_hooks, $1; 547 | } 548 | } 549 | closedir $dh; 550 | my $available_shell_hooks_text = join('|', @available_shell_hooks); 551 | 552 | say <)" 555 | # to your local profile file. 556 | # (often ~/.bash_profile, ~/.zsh_profile or ~/.profile) 557 | # This can be easily done using: 558 | 559 | echo 'eval "\$($brew_exec init <$available_shell_hooks_text>)"' >> ~/.profile 560 | 561 | # On Fish: 562 | 563 | echo '$brew_exec init Fish | source' >> ~/.config/fish/config.fish 564 | 565 | EOT 566 | } 567 | } 568 | } 569 | 570 | sub do_exec { 571 | my ($program, $args) = @_; 572 | 573 | my $target = which($program, get_version()); 574 | 575 | # Run. 576 | exec { $target } ($target, @$args); 577 | die "Executing $target failed with: $!"; 578 | } 579 | 580 | -------------------------------------------------------------------------------- /bin/rakudobrew.bat: -------------------------------------------------------------------------------- 1 | @perl %~dp0rakudobrew %* -------------------------------------------------------------------------------- /lib/File/Which.pm: -------------------------------------------------------------------------------- 1 | package File::Which; 2 | 3 | use strict; 4 | use warnings; 5 | use Exporter (); 6 | use File::Spec (); 7 | 8 | # ABSTRACT: Perl implementation of the which utility as an API 9 | our $VERSION = '1.23'; # VERSION 10 | 11 | 12 | our @ISA = 'Exporter'; 13 | our @EXPORT = 'which'; 14 | our @EXPORT_OK = 'where'; 15 | 16 | use constant IS_VMS => ($^O eq 'VMS'); 17 | use constant IS_MAC => ($^O eq 'MacOS'); 18 | use constant IS_WIN => ($^O eq 'MSWin32' or $^O eq 'dos' or $^O eq 'os2'); 19 | use constant IS_DOS => IS_WIN(); 20 | use constant IS_CYG => ($^O eq 'cygwin' || $^O eq 'msys'); 21 | 22 | our $IMPLICIT_CURRENT_DIR = IS_WIN || IS_VMS || IS_MAC; 23 | 24 | # For Win32 systems, stores the extensions used for 25 | # executable files 26 | # For others, the empty string is used 27 | # because 'perl' . '' eq 'perl' => easier 28 | my @PATHEXT = (''); 29 | if ( IS_WIN ) { 30 | # WinNT. PATHEXT might be set on Cygwin, but not used. 31 | if ( $ENV{PATHEXT} ) { 32 | push @PATHEXT, split ';', $ENV{PATHEXT}; 33 | } else { 34 | # Win9X or other: doesn't have PATHEXT, so needs hardcoded. 35 | push @PATHEXT, qw{.com .exe .bat}; 36 | } 37 | } elsif ( IS_VMS ) { 38 | push @PATHEXT, qw{.exe .com}; 39 | } elsif ( IS_CYG ) { 40 | # See this for more info 41 | # http://cygwin.com/cygwin-ug-net/using-specialnames.html#pathnames-exe 42 | push @PATHEXT, qw{.exe .com}; 43 | } 44 | 45 | 46 | sub which { 47 | my ($exec) = @_; 48 | 49 | return undef unless defined $exec; 50 | return undef if $exec eq ''; 51 | 52 | my $all = wantarray; 53 | my @results = (); 54 | 55 | # check for aliases first 56 | if ( IS_VMS ) { 57 | my $symbol = `SHOW SYMBOL $exec`; 58 | chomp($symbol); 59 | unless ( $? ) { 60 | return $symbol unless $all; 61 | push @results, $symbol; 62 | } 63 | } 64 | if ( IS_MAC ) { 65 | my @aliases = split /\,/, $ENV{Aliases}; 66 | foreach my $alias ( @aliases ) { 67 | # This has not been tested!! 68 | # PPT which says MPW-Perl cannot resolve `Alias $alias`, 69 | # let's just hope it's fixed 70 | if ( lc($alias) eq lc($exec) ) { 71 | chomp(my $file = `Alias $alias`); 72 | last unless $file; # if it failed, just go on the normal way 73 | return $file unless $all; 74 | push @results, $file; 75 | # we can stop this loop as if it finds more aliases matching, 76 | # it'll just be the same result anyway 77 | last; 78 | } 79 | } 80 | } 81 | 82 | return $exec 83 | if !IS_VMS and !IS_MAC and !IS_WIN and $exec =~ /\// and -f $exec and -x $exec; 84 | 85 | my @path; 86 | if($^O eq 'MSWin32') { 87 | # File::Spec (at least recent versions) 88 | # add the implicit . for you on MSWin32, 89 | # but we may or may not want to include 90 | # that. 91 | @path = split(';', $ENV{PATH}); 92 | s/"//g for @path; 93 | @path = grep length, @path; 94 | } else { 95 | @path = File::Spec->path; 96 | } 97 | if ( $IMPLICIT_CURRENT_DIR ) { 98 | unshift @path, File::Spec->curdir; 99 | } 100 | 101 | foreach my $base ( map { File::Spec->catfile($_, $exec) } @path ) { 102 | for my $ext ( @PATHEXT ) { 103 | my $file = $base.$ext; 104 | 105 | # We don't want dirs (as they are -x) 106 | next if -d $file; 107 | 108 | if ( 109 | # Executable, normal case 110 | -x _ 111 | or ( 112 | # MacOS doesn't mark as executable so we check -e 113 | IS_MAC 114 | || 115 | ( 116 | ( IS_WIN or IS_CYG ) 117 | and 118 | grep { 119 | $file =~ /$_\z/i 120 | } @PATHEXT[1..$#PATHEXT] 121 | ) 122 | # DOSish systems don't pass -x on 123 | # non-exe/bat/com files. so we check -e. 124 | # However, we don't want to pass -e on files 125 | # that aren't in PATHEXT, like README. 126 | and -e _ 127 | ) 128 | ) { 129 | return $file unless $all; 130 | push @results, $file; 131 | } 132 | } 133 | } 134 | 135 | if ( $all ) { 136 | return @results; 137 | } else { 138 | return undef; 139 | } 140 | } 141 | 142 | 143 | sub where { 144 | # force wantarray 145 | my @res = which($_[0]); 146 | return @res; 147 | } 148 | 149 | 1; 150 | 151 | __END__ 152 | 153 | =pod 154 | 155 | =encoding UTF-8 156 | 157 | =head1 NAME 158 | 159 | File::Which - Perl implementation of the which utility as an API 160 | 161 | =head1 VERSION 162 | 163 | version 1.23 164 | 165 | =head1 SYNOPSIS 166 | 167 | use File::Which; # exports which() 168 | use File::Which qw(which where); # exports which() and where() 169 | 170 | my $exe_path = which 'perldoc'; 171 | 172 | my @paths = where 'perl'; 173 | # Or 174 | my @paths = which 'perl'; # an array forces search for all of them 175 | 176 | =head1 DESCRIPTION 177 | 178 | L finds the full or relative paths to executable programs on 179 | the system. This is normally the function of C utility. C is 180 | typically implemented as either a program or a built in shell command. On 181 | some platforms, such as Microsoft Windows it is not provided as part of the 182 | core operating system. This module provides a consistent API to this 183 | functionality regardless of the underlying platform. 184 | 185 | The focus of this module is correctness and portability. As a consequence 186 | platforms where the current directory is implicitly part of the search path 187 | such as Microsoft Windows will find executables in the current directory, 188 | whereas on platforms such as UNIX where this is not the case executables 189 | in the current directory will only be found if the current directory is 190 | explicitly added to the path. 191 | 192 | If you need a portable C on the command line in an environment that 193 | does not provide it, install L which provides a command line 194 | interface to this API. 195 | 196 | =head2 Implementations 197 | 198 | L searches the directories of the user's C (the current 199 | implementation uses L to determine the correct C), 200 | looking for executable files having the name specified as a parameter to 201 | L. Under Win32 systems, which do not have a notion of directly 202 | executable files, but uses special extensions such as C<.exe> and C<.bat> 203 | to identify them, C takes extra steps to assure that 204 | you will find the correct file (so for example, you might be searching for 205 | C, it'll try F, F, etc.) 206 | 207 | =head3 Linux, *BSD and other UNIXes 208 | 209 | There should not be any surprises here. The current directory will not be 210 | searched unless it is explicitly added to the path. 211 | 212 | =head3 Modern Windows (including NT, XP, Vista, 7, 8, 10 etc) 213 | 214 | Windows NT has a special environment variable called C, which is used 215 | by the shell to look for executable files. Usually, it will contain a list in 216 | the form C<.EXE;.BAT;.COM;.JS;.VBS> etc. If C finds such an 217 | environment variable, it parses the list and uses it as the different 218 | extensions. 219 | 220 | =head3 Cygwin 221 | 222 | Cygwin provides a Unix-like environment for Microsoft Windows users. In most 223 | ways it works like other Unix and Unix-like environments, but in a few key 224 | aspects it works like Windows. As with other Unix environments, the current 225 | directory is not included in the search unless it is explicitly included in 226 | the search path. Like on Windows, files with C<.EXE> or <.BAT> extensions will 227 | be discovered even if they are not part of the query. C<.COM> or extensions 228 | specified using the C environment variable will NOT be discovered 229 | without the fully qualified name, however. 230 | 231 | =head3 Windows ME, 98, 95, MS-DOS, OS/2 232 | 233 | This set of operating systems don't have the C variable, and usually 234 | you will find executable files there with the extensions C<.exe>, C<.bat> and 235 | (less likely) C<.com>. C uses this hardcoded list if it's running 236 | under Win32 but does not find a C variable. 237 | 238 | As of 2015 none of these platforms are tested frequently (or perhaps ever), 239 | but the current maintainer is determined not to intentionally remove support 240 | for older operating systems. 241 | 242 | =head3 VMS 243 | 244 | Same case as Windows 9x: uses C<.exe> and C<.com> (in that order). 245 | 246 | As of 2015 the current maintainer does not test on VMS, and is in fact not 247 | certain it has ever been tested on VMS. If this platform is important to you 248 | and you can help me verify and or support it on that platform please contact 249 | me. 250 | 251 | =head1 FUNCTIONS 252 | 253 | =head2 which 254 | 255 | my $path = which $short_exe_name; 256 | my @paths = which $short_exe_name; 257 | 258 | Exported by default. 259 | 260 | C<$short_exe_name> is the name used in the shell to call the program (for 261 | example, C). 262 | 263 | If it finds an executable with the name you specified, C will return 264 | the absolute path leading to this executable (for example, F or 265 | F). 266 | 267 | If it does I find the executable, it returns C. 268 | 269 | If C is called in list context, it will return I the 270 | matches. 271 | 272 | =head2 where 273 | 274 | my @paths = where $short_exe_name; 275 | 276 | Not exported by default. 277 | 278 | Same as L in array context. Similar to the C csh 279 | built-in command or C command for platforms that support the 280 | C<-a> option. Will return an array containing all the path names 281 | matching C<$short_exe_name>. 282 | 283 | =head1 GLOBALS 284 | 285 | =head2 $IMPLICIT_CURRENT_DIR 286 | 287 | True if the current directory is included in the search implicitly on 288 | whatever platform you are using. Normally the default is reasonable, 289 | but on Windows the current directory is included implicitly for older 290 | shells like C and C, but not for newer shells 291 | like PowerShell. If you overrule this default, you should ALWAYS 292 | localize the variable to the tightest scope possible, since setting 293 | this variable from a module can affect other modules. Thus on Windows 294 | you can get the correct result if the user is running either C 295 | or PowerShell on Windows you can do this: 296 | 297 | use File::Which qw( which ); 298 | use Shell::Guess; 299 | 300 | my $path = do { 301 | my $is_power = Shell::Guess->running_shell->is_power; 302 | local $File::Which::IMPLICIT_CURRENT_DIR = !$is_power; 303 | which 'foo'; 304 | }; 305 | 306 | For a variety of reasons it is difficult to accurately compute the 307 | shell that a user is using, but L makes a reasonable 308 | effort. 309 | 310 | =head1 CAVEATS 311 | 312 | This module has no non-core requirements for Perl 5.6.2 and better. 313 | 314 | This module is fully supported back to Perl 5.8.1. It may work on 5.8.0. 315 | It should work on Perl 5.6.x and I may even test on 5.6.2. I will accept 316 | patches to maintain compatibility for such older Perls, but you may 317 | need to fix it on 5.6.x / 5.8.0 and send me a patch. 318 | 319 | Not tested on VMS although there is platform specific code 320 | for those. Anyone who haves a second would be very kind to send me a 321 | report of how it went. 322 | 323 | =head1 SUPPORT 324 | 325 | Bugs should be reported via the GitHub issue tracker 326 | 327 | L 328 | 329 | For other issues, contact the maintainer. 330 | 331 | =head1 SEE ALSO 332 | 333 | =over 4 334 | 335 | =item L, L 336 | 337 | Command line interface to this module. 338 | 339 | =item L 340 | 341 | This module provides (among other things) a C function, which is 342 | similar to C. It is a much heavier module since it does a lot more, 343 | and if you use C it pulls in L. This combination 344 | may be overkill for applications which do not need L's complicated 345 | interface for running programs, or do not need the memory overhead required 346 | for installing Perl modules. 347 | 348 | At least some older versions will find executables in the current directory, 349 | even if the current directory is not in the search path (which is the default 350 | on modern Unix). 351 | 352 | C converts directory path name to the 8.3 version on Windows using 353 | C in some cases. This is frequently useful for tools 354 | that just need to run something using C in scalar mode, but may be 355 | inconvenient for tools like L where user readability is a premium. 356 | Relying on C to produce filenames without spaces 357 | is problematic, as 8.3 filenames can be turned off with tweaks to the 358 | registry (see L). 359 | 360 | =item L 361 | 362 | This module purports to "check that a command is available", but does not 363 | provide any documentation on how you might use it. 364 | 365 | =back 366 | 367 | =head1 AUTHORS 368 | 369 | =over 4 370 | 371 | =item * 372 | 373 | Per Einar Ellefsen 374 | 375 | =item * 376 | 377 | Adam Kennedy 378 | 379 | =item * 380 | 381 | Graham Ollis 382 | 383 | =back 384 | 385 | =head1 COPYRIGHT AND LICENSE 386 | 387 | This software is copyright (c) 2002 by Per Einar Ellefsen . 388 | 389 | This is free software; you can redistribute it and/or modify it under 390 | the same terms as the Perl 5 programming language system itself. 391 | 392 | =cut 393 | -------------------------------------------------------------------------------- /lib/Rakudobrew/Build.pm: -------------------------------------------------------------------------------- 1 | package Rakudobrew::Build; 2 | require Exporter; 3 | our @ISA = qw( Exporter ); 4 | our @EXPORT = qw(); 5 | 6 | use strict; 7 | use warnings; 8 | use 5.010; 9 | use File::Spec::Functions qw(catdir updir); 10 | use Cwd qw(cwd); 11 | use Rakudobrew::Variables; 12 | use Rakudobrew::Tools; 13 | use Rakudobrew::VersionHandling; 14 | 15 | sub available_rakudos { 16 | my @output = qx|$GIT ls-remote --tags $git_repos{rakudo}|; 17 | my @tags = grep(m{refs/tags/([^\^]+)\^\{\}}, @output); 18 | @tags = map(m{tags/([^\^]+)\^}, @tags); 19 | @tags = grep(/^\d/, @tags); 20 | return sort(@tags), 'master'; 21 | } 22 | 23 | sub build_impl { 24 | my ($impl, $ver, $configure_opts) = @_; 25 | 26 | my $name = "$impl-$ver"; 27 | $name = $impl if $impl eq 'moar-blead' && $ver eq 'master'; 28 | 29 | chdir $versions_dir; 30 | unless (-d $name) { 31 | for(@{$impls{$impl}{need_repo}}) { 32 | update_git_reference($_); 33 | } 34 | run "$GIT clone --reference \"$git_reference/rakudo\" $git_repos{rakudo} $name"; 35 | } 36 | chdir $name; 37 | run "$GIT fetch"; 38 | # when people say 'build somebranch', they usually mean 'build origin/somebranch' 39 | my $ver_to_checkout = $ver; 40 | eval { 41 | run "$GIT rev-parse -q --verify origin/$ver"; 42 | $ver_to_checkout = "origin/$ver"; 43 | }; 44 | run "$GIT checkout $ver_to_checkout"; 45 | 46 | run $impls{$impl}{configure} . " $configure_opts"; 47 | } 48 | 49 | sub determine_make { 50 | my $makefile = shift; 51 | $makefile = slurp($makefile); 52 | 53 | if($makefile =~ /^MAKE\s*=\s*(\w+)\s*$/m) { 54 | return $1; 55 | } 56 | else { 57 | say STDERR "Couldn't determine correct make program. Aborting."; 58 | exit 1; 59 | } 60 | } 61 | 62 | sub build_triple { 63 | my ($rakudo_ver, $nqp_ver, $moar_ver) = @_; 64 | my $impl = "moar"; 65 | $rakudo_ver //= 'HEAD'; 66 | $nqp_ver //= 'HEAD'; 67 | $moar_ver //= 'HEAD'; 68 | chdir $versions_dir; 69 | my $name = "$impl-$rakudo_ver-$nqp_ver-$moar_ver"; 70 | unless (-d $name) { 71 | update_git_reference('rakudo'); 72 | run "$GIT clone --reference \"$git_reference/rakudo\" $git_repos{rakudo} $name"; 73 | } 74 | chdir $name; 75 | run "$GIT pull"; 76 | run "$GIT checkout $rakudo_ver"; 77 | if (-e 'Makefile') { 78 | run(determine_make('Makefile'), 'install'); 79 | } 80 | 81 | unless (-d "nqp") { 82 | update_git_reference('nqp'); 83 | run "$GIT clone --reference \"$git_reference/nqp\" $git_repos{nqp}"; 84 | } 85 | chdir "nqp"; 86 | run "$GIT pull"; 87 | run "$GIT checkout $nqp_ver"; 88 | 89 | unless (-d "MoarVM") { 90 | update_git_reference('MoarVM'); 91 | run "$GIT clone --reference \"$git_reference/MoarVM\" $git_repos{MoarVM}"; 92 | } 93 | chdir "MoarVM"; 94 | run "$GIT pull"; 95 | run "$GIT checkout $moar_ver"; 96 | run "$PERL5 Configure.pl --prefix=" . catdir(updir(), updir(), 'install') . ' --make-install'; 97 | 98 | chdir updir(); 99 | run "$PERL5 Configure.pl --backend=moar --prefix=" . catdir(updir(), 'install') . ' --make-install'; 100 | 101 | chdir updir(); 102 | run "$PERL5 Configure.pl --backend=moar --make-install"; 103 | 104 | if (-d 'zef') { 105 | say "Updating zef as well"; 106 | build_zef($name); 107 | } 108 | 109 | return $name; 110 | } 111 | 112 | sub build_zef { 113 | my $version = shift; 114 | chdir catdir($versions_dir, $version); 115 | unless (-d 'zef') { 116 | run "$GIT clone $git_repos{zef}"; 117 | } 118 | chdir 'zef'; 119 | run "$GIT pull -q"; 120 | run "$GIT checkout"; 121 | run which('perl6', $version) . " -Ilib bin/zef test ."; 122 | run which('perl6', $version) . " -Ilib bin/zef --/test --force install ."; 123 | } 124 | 125 | sub update_git_reference { 126 | my $repo = shift; 127 | my $back = cwd(); 128 | print "Update git reference: $repo\n"; 129 | chdir $git_reference; 130 | unless (-d $repo) { 131 | run "$GIT clone $git_repos{$repo} $repo"; 132 | } 133 | chdir $repo; 134 | run "$GIT pull"; 135 | chdir $back; 136 | } 137 | 138 | sub available_backends { 139 | map {$_->{name}} sort {$a->{weight} <=> $b->{weight}} values %impls; 140 | } 141 | 142 | 143 | 1; 144 | 145 | -------------------------------------------------------------------------------- /lib/Rakudobrew/ShellHook.pm: -------------------------------------------------------------------------------- 1 | package Rakudobrew::ShellHook; 2 | require Exporter; 3 | our @ISA = qw( Exporter ); 4 | our @EXPORT = qw(); 5 | 6 | use strict; 7 | use warnings; 8 | use 5.010; 9 | use File::Spec::Functions qw(catdir updir); 10 | use Cwd qw(cwd); 11 | use Rakudobrew::Variables; 12 | use Rakudobrew::Tools; 13 | use Rakudobrew::VersionHandling; 14 | 15 | sub print_shellmod_code { 16 | no strict 'refs'; 17 | my @params = @_; 18 | my $shell = shift(@params); 19 | my $command = shift(@params) // ''; 20 | my $mode = get_brew_mode(1); 21 | 22 | eval "require Rakudobrew::ShellHook::$shell"; 23 | if ($@) { 24 | die "Shell hook '$shell' not found."; 25 | } 26 | 27 | if ($mode eq 'shim') { 28 | if ($command eq 'shell' && @params) { 29 | if ($params[0] eq '--unset') { 30 | say "Rakudobrew::ShellHook::${shell}::get_shell_unsetter_code"->(); 31 | } 32 | else { 33 | say "Rakudobrew::ShellHook::${shell}::get_shell_setter_code"->($params[0]); 34 | } 35 | } 36 | elsif ($command eq 'mode') { # just switched to shim mode 37 | my $path = $ENV{PATH}; 38 | $path = clean_path($path); 39 | $path = $shim_dir . ':' . $path; 40 | say "Rakudobrew::ShellHook::${shell}::get_path_setter_code"->($path); 41 | } 42 | } 43 | else { # get_brew_mode() eq 'env' 44 | my $version = get_version(); 45 | my $path = $ENV{PATH}; 46 | $path = clean_path($path); 47 | if ($version ne 'system') { 48 | $path = join(':', get_bin_paths($version), $path); 49 | } 50 | if ($path ne $ENV{PATH}) { 51 | say "Rakudobrew::ShellHook::${shell}::get_path_setter_code"->($path); 52 | } 53 | } 54 | } 55 | 56 | sub clean_path { 57 | my $path = shift; 58 | my $also_clean_path = shift; 59 | 60 | my $sep = $^O =~ /win32/i ? ';' : ':'; 61 | 62 | my @paths; 63 | for my $version (get_versions()) { 64 | push @paths, get_bin_paths($version) if $version ne 'system'; 65 | } 66 | push @paths, $versions_dir; 67 | push @paths, $shim_dir; 68 | push @paths, $also_clean_path if $also_clean_path; 69 | my $paths_regex = join "|", @paths; 70 | 71 | my $old_path; 72 | do { 73 | $old_path = $path; 74 | $path =~ s/^($paths_regex)[^$sep]*$//g; 75 | $path =~ s/^($paths_regex)[^$sep]*$sep//g; 76 | $path =~ s/$sep($paths_regex)[^$sep]*$//g; 77 | $path =~ s/$sep($paths_regex)[^$sep]*$sep/$sep/g; 78 | } until $path eq $old_path; 79 | return $path; 80 | } 81 | 82 | 1; 83 | -------------------------------------------------------------------------------- /lib/Rakudobrew/ShellHook/Bash.pm: -------------------------------------------------------------------------------- 1 | package Rakudobrew::ShellHook::Bash; 2 | use strict; 3 | use warnings; 4 | use 5.010; 5 | use File::Spec::Functions qw(catdir splitpath); 6 | use FindBin qw($RealBin $RealScript); 7 | 8 | use Rakudobrew::Variables; 9 | use Rakudobrew::Tools; 10 | use Rakudobrew::VersionHandling; 11 | use Rakudobrew::ShellHook; 12 | use Rakudobrew::Build; 13 | 14 | sub get_init_code { 15 | my $path = $ENV{PATH}; 16 | $path = Rakudobrew::ShellHook::clean_path($path, $RealBin); 17 | $path = "$RealBin:$path"; 18 | if (get_brew_mode() eq 'env') { 19 | if (get_global_version() && get_global_version() ne 'system') { 20 | $path = join(':', get_bin_paths(get_global_version()), $path); 21 | } 22 | } 23 | else { # get_brew_mode() eq 'shim' 24 | $path = join(':', $shim_dir, $path); 25 | } 26 | 27 | return < 0 && substr($candidate, -1) ne '/'; 106 | push @completions, $candidate; 107 | } 108 | closedir $dh; 109 | say join(' ', @completions); 110 | } 111 | } 112 | sub completion_options { 113 | my $index = shift; 114 | my @words = @_; 115 | 116 | if($index == 3 && $words[1] eq 'register') { 117 | say 'compopt -o nospace'; 118 | } 119 | else { 120 | say ''; 121 | } 122 | } 123 | 124 | 1; 125 | 126 | -------------------------------------------------------------------------------- /lib/Rakudobrew/ShellHook/Fish.pm: -------------------------------------------------------------------------------- 1 | package Rakudobrew::ShellHook::Fish; 2 | use strict; 3 | use warnings; 4 | use 5.010; 5 | use File::Spec::Functions qw(catdir splitpath); 6 | use FindBin qw($RealBin $RealScript); 7 | 8 | use Rakudobrew::Variables; 9 | use Rakudobrew::Tools; 10 | use Rakudobrew::VersionHandling; 11 | use Rakudobrew::ShellHook; 12 | use Rakudobrew::Build; 13 | 14 | sub get_init_code { 15 | my $path = $ENV{PATH}; 16 | $path = Rakudobrew::ShellHook::clean_path($path, $RealBin); 17 | 18 | my @path_components = split /:/, $path; 19 | @path_components = map { "'$_'" } @path_components; 20 | 21 | unshift @path_components, "'$RealBin'"; 22 | 23 | $path =~ s/:/ /g; 24 | if (get_brew_mode() eq 'env') { 25 | if (get_global_version() && get_global_version() ne 'system') { 26 | unshift @path_components, map({ "'$_'" } get_bin_paths(get_global_version())); 27 | } 28 | } 29 | else { # get_brew_mode() eq 'shim' 30 | unshift @path_components, "'$shim_dir'"; 31 | } 32 | 33 | $path = join(' ', @path_components); 34 | 35 | return <; 22 | close($fh); 23 | return $ret; 24 | } 25 | 26 | sub spurt { 27 | my ($file, $cont) = @_; 28 | open(my $fh, '>', $file); 29 | say $fh $cont; 30 | close($fh); 31 | } 32 | 33 | sub trim { 34 | my $text = shift; 35 | $text =~ s/^\s+|\s+$//g; 36 | return $text; 37 | } 38 | 39 | sub check_prog_name_match { 40 | my ($prog, $filename) = @_; 41 | my ($basename, undef, undef) = my_fileparse($filename); 42 | return $prog =~ /^\Q$basename\E\z/i; 43 | } 44 | 45 | sub uniq { 46 | my %seen; 47 | return grep { !$seen{$_}++ } @_; 48 | } 49 | 50 | sub slurp_dir { 51 | my $name = shift; 52 | opendir(my $dh, $name) or return; 53 | my @ret; 54 | while (my $entry = readdir $dh) { 55 | next if $entry =~ /^\./; 56 | next if !-f catfile($name, $entry); 57 | push @ret, $entry; 58 | } 59 | closedir $dh; 60 | return @ret; 61 | } 62 | 63 | sub my_fileparse { 64 | return fileparse(shift, ('.dll.lib', qr/\.[^.]+/)); 65 | } 66 | 67 | 1; 68 | 69 | -------------------------------------------------------------------------------- /lib/Rakudobrew/Variables.pm: -------------------------------------------------------------------------------- 1 | package Rakudobrew::Variables; 2 | require Exporter; 3 | our @ISA = qw( Exporter ); 4 | our @EXPORT = qw( $brew_name $env_var $local_filename $prefix $versions_dir $shim_dir $git_reference $GIT $GIT_PROTO $PERL5 %git_repos %impls ); 5 | 6 | use strict; 7 | use warnings; 8 | use 5.010; 9 | 10 | use FindBin qw($RealBin); 11 | use File::Spec::Functions qw(catfile catdir updir); 12 | use Cwd 'abs_path'; 13 | 14 | our $brew_name = 'rakudobrew'; 15 | our $env_var = 'PL6ENV_VERSION'; 16 | our $local_filename = '.perl6-version'; 17 | 18 | our $prefix = abs_path(catdir($RealBin, updir())); 19 | our $versions_dir = catdir($prefix, 'versions'); 20 | our $shim_dir = catdir($prefix, 'shims'); 21 | our $git_reference = catdir($prefix, 'git_reference'); 22 | 23 | our $GIT = $ENV{GIT_BINARY} // 'git'; 24 | our $GIT_PROTO = $ENV{GIT_PROTOCOL} // 'git'; 25 | our $PERL5 = $^X; 26 | 27 | sub get_git_url { 28 | my ($protocol, $host, $user, $project) = @_; 29 | if ($protocol eq "ssh") { 30 | return "git\@${host}:${user}/${project}.git"; 31 | } else { 32 | return "${protocol}://${host}/${user}/${project}.git"; 33 | } 34 | } 35 | 36 | our %git_repos = ( 37 | rakudo => get_git_url($GIT_PROTO, 'github.com', 'rakudo', 'rakudo'), 38 | MoarVM => get_git_url($GIT_PROTO, 'github.com', 'MoarVM', 'MoarVM'), 39 | nqp => get_git_url($GIT_PROTO, 'github.com', 'perl6', 'nqp'), 40 | zef => get_git_url($GIT_PROTO, 'github.com', 'ugexe', 'zef'), 41 | ); 42 | 43 | our %impls = ( 44 | jvm => { 45 | name => "jvm", 46 | weight => 20, 47 | configure => "$PERL5 Configure.pl --backends=jvm --gen-nqp --git-reference=\"$git_reference\" --make-install", 48 | need_repo => ['rakudo', 'nqp'], 49 | }, 50 | moar => { 51 | name => "moar", 52 | weight => 30, 53 | configure => "$PERL5 Configure.pl --backends=moar --gen-moar --git-reference=\"$git_reference\" --make-install", 54 | need_repo => ['rakudo', 'nqp', 'MoarVM'], 55 | }, 56 | 'moar-blead' => { 57 | name => "moar-blead", 58 | weight => 35, 59 | configure => "$PERL5 Configure.pl --backends=moar --gen-moar=master --gen-nqp=master --git-reference=\"$git_reference\" --make-install", 60 | need_repo => ['rakudo', 'nqp', 'MoarVM'], 61 | }, 62 | ); 63 | 64 | 1; 65 | 66 | -------------------------------------------------------------------------------- /lib/Rakudobrew/VersionHandling.pm: -------------------------------------------------------------------------------- 1 | package Rakudobrew::VersionHandling; 2 | require Exporter; 3 | our @ISA = qw( Exporter ); 4 | our @EXPORT = qw( 5 | get_versions 6 | get_version 7 | version_exists 8 | is_registered_version 9 | get_version_path 10 | get_shell_version 11 | get_local_version set_local_version 12 | get_global_version set_global_version 13 | set_brew_mode get_brew_mode get_brew_mode_shell validate_brew_mode 14 | which whence 15 | get_bin_paths 16 | rehash 17 | ); 18 | 19 | use strict; 20 | use warnings; 21 | use 5.010; 22 | use File::Spec::Functions qw(catfile catdir splitdir splitpath catpath canonpath); 23 | use Cwd qw(realpath); 24 | use File::Which qw(); 25 | use Rakudobrew::Variables; 26 | use Rakudobrew::Tools; 27 | 28 | sub get_versions { 29 | opendir(my $dh, $versions_dir); 30 | my @versions = ( 31 | 'system', 32 | sort({ $a cmp $b } 33 | grep({ /^[^.]/ } readdir($dh))) 34 | ); 35 | closedir($dh); 36 | return @versions; 37 | } 38 | 39 | sub get_shell_version { 40 | # Check for shell version by looking for $PL6ENV_VERSION the environment. 41 | if (defined $ENV{$env_var}) { 42 | my $version = $ENV{$env_var}; 43 | if (version_exists($version)) { 44 | return $version; 45 | } 46 | else { 47 | say STDERR "Version '$version' is set via the PL6ENV_VERSION environment variable."; 48 | say STDERR "This version is not installed. Ignoring."; 49 | say STDERR ''; 50 | return undef; 51 | } 52 | } 53 | else { 54 | return undef; 55 | } 56 | } 57 | 58 | sub get_local_version { 59 | my ($vol, $path, undef) = splitpath(realpath(), 1); 60 | my @fragments = splitdir($path); 61 | while (@fragments) { 62 | my $filepath = catpath($vol, catdir(@fragments), $local_filename); 63 | if (-f $filepath) { 64 | my $version = trim(slurp($filepath)); 65 | if(version_exists($version)) { 66 | return $version; 67 | } 68 | else { 69 | say STDERR "Version '$version' is given in the"; 70 | say STDERR "$filepath"; 71 | say STDERR "file. This version is not installed. Ignoring."; 72 | say STDERR ''; 73 | } 74 | } 75 | pop @fragments; 76 | } 77 | return undef; 78 | } 79 | 80 | sub set_local_version { 81 | spurt($local_filename, shift); 82 | } 83 | 84 | sub get_global_version { 85 | if (!-e catfile($prefix, 'CURRENT')) { 86 | set_global_version('system', 1); 87 | } 88 | my $cur = slurp(catfile($prefix, 'CURRENT')); 89 | chomp $cur; 90 | return $cur; 91 | } 92 | 93 | sub set_global_version { 94 | my $version = shift; 95 | my $silent = shift; 96 | say "Switching to $version" unless $silent; 97 | spurt(catfile($prefix, 'CURRENT'), $version); 98 | } 99 | 100 | sub get_version { 101 | my $version = get_shell_version(); 102 | return $version if defined $version; 103 | 104 | if (get_brew_mode() eq 'shim') { 105 | # Local version is only supported in shim mode. 106 | # Check for local version by looking for a `.perl6-version` file in the current and parent folders. 107 | $version = get_local_version(); 108 | return $version if defined $version; 109 | } 110 | 111 | # Check for global version by looking at `$prefix/CURRENT` (`$prefix/version`) 112 | return get_global_version(); 113 | } 114 | 115 | sub set_brew_mode { 116 | my $mode = shift; 117 | if ($mode eq 'env') { 118 | if ($^O =~ /win32/i) { 119 | say STDERR "env-mode is currently not supported on Windows."; 120 | return; 121 | } 122 | spurt(catfile($prefix, 'MODE'), 'env'); 123 | } 124 | elsif ($mode eq 'shim') { 125 | spurt(catfile($prefix, 'MODE'), 'shim'); 126 | rehash(); 127 | } 128 | else { 129 | say STDERR "Mode must either be 'env' or 'shim'"; 130 | } 131 | } 132 | 133 | sub get_brew_mode { 134 | my $silent = shift; 135 | if (!-e catfile($prefix, 'MODE')) { 136 | if ($^O =~ /win32/i) { 137 | spurt(catfile($prefix, 'MODE'), 'shim'); 138 | } 139 | else { 140 | spurt(catfile($prefix, 'MODE'), 'env'); 141 | } 142 | } 143 | 144 | my $mode = trim(slurp(catfile($prefix, 'MODE'))); 145 | 146 | if ($^O =~ /win32/i && $mode eq 'env') { 147 | say STDERR 'env-mode is not supported on Windows' unless $silent; 148 | say STDERR 'Resetting to shim-mode' unless $silent; 149 | set_brew_mode('shim'); 150 | $mode = 'shim'; 151 | } 152 | elsif ($mode ne 'env' && $mode ne 'shim') { 153 | say STDERR 'Invalid mode found: ' . $mode unless $silent; 154 | say STDERR 'Resetting to env-mode' unless $silent; 155 | set_brew_mode('env'); 156 | $mode = 'env'; 157 | } 158 | 159 | return $mode; 160 | } 161 | 162 | sub validate_brew_mode { 163 | if (get_brew_mode() eq 'env') { 164 | say STDERR "This command is not available in 'env' mode. Switch to to 'shim' mode using '$brew_name mode shim'"; 165 | exit 1; 166 | } 167 | } 168 | 169 | sub version_exists { 170 | my $version = shift; 171 | return undef if !defined $version; 172 | my %versionsMap = map { $_ => 1 } get_versions(); 173 | return exists($versionsMap{$version}); 174 | } 175 | 176 | sub is_registered_version { 177 | my $version = shift; 178 | my $version_file = catdir($versions_dir, $version); 179 | if (-f $version_file) { 180 | return 1; 181 | } 182 | else { 183 | return 0; 184 | } 185 | } 186 | 187 | sub get_version_path { 188 | my $version = shift; 189 | my $version_path = catdir($versions_dir, $version); 190 | return catdir($version_path, 'install') if -d $version_path; 191 | return trim(slurp($version_path)) if -f $version_path; 192 | die "Invalid version found: $version"; 193 | } 194 | 195 | 196 | sub which { 197 | my $prog = shift; 198 | my $version = shift; 199 | 200 | my $target; { 201 | if ($version eq 'system') { 202 | my @targets = File::Which::which($prog); 203 | @targets = map({ 204 | $_ =~ s|\\|/|g; 205 | $_ = canonpath($_); 206 | } @targets); 207 | 208 | my $normalized_shim_dir = $shim_dir; 209 | $normalized_shim_dir =~ s|\\|/|g; 210 | $normalized_shim_dir = canonpath($normalized_shim_dir); 211 | 212 | @targets = grep({ 213 | my ($volume,$directories,$file) = splitpath( $_ ); 214 | my $target_dir = catpath($volume, $directories); 215 | $target_dir = canonpath($target_dir); 216 | $target_dir ne $normalized_shim_dir; 217 | } @targets); 218 | 219 | $target = $targets[0] if @targets; 220 | } 221 | elsif ($^O =~ /win32/i) { 222 | # The postfix of an executable on Windows is often unclear. 223 | # Thus we look for files with a basename matching the given 224 | # name. 225 | my @results = (); 226 | my @dirs = get_bin_paths($version); 227 | for my $dir (@dirs) { 228 | my @files = slurp_dir($dir); 229 | for my $file (@files) { 230 | if(check_prog_name_match($prog, $file)) { 231 | push @results, catfile($dir, $file); 232 | } 233 | } 234 | } 235 | @results = sort { 236 | # Prefer .exe > .bat > .p6 > .pl6 > .pl > nothing > anything else 237 | my (undef, undef, $suffix_a) = my_fileparse($a); 238 | my (undef, undef, $suffix_b) = my_fileparse($b); 239 | return -1 if $suffix_a eq '.exe' && $suffix_b ne '.exe'; 240 | return 1 if $suffix_a ne '.exe' && $suffix_b eq '.exe'; 241 | return $a cmp $b if $suffix_a eq '.exe' && $suffix_b eq '.exe'; 242 | return -1 if $suffix_a eq '.bat' && $suffix_b ne '.bat'; 243 | return 1 if $suffix_a ne '.bat' && $suffix_b eq '.bat'; 244 | return $a cmp $b if $suffix_a eq '.bat' && $suffix_b eq '.bat'; 245 | return -1 if $suffix_a eq '.p6' && $suffix_b ne '.p6'; 246 | return 1 if $suffix_a ne '.p6' && $suffix_b eq '.p6'; 247 | return $a cmp $b if $suffix_a eq '.p6' && $suffix_b eq '.p6'; 248 | return -1 if $suffix_a eq '.pl6' && $suffix_b ne '.pl6'; 249 | return 1 if $suffix_a ne '.pl6' && $suffix_b eq '.pl6'; 250 | return $a cmp $b if $suffix_a eq '.pl6' && $suffix_b eq '.pl6'; 251 | return -1 if $suffix_a eq '.pl' && $suffix_b ne '.pl'; 252 | return 1 if $suffix_a ne '.pl' && $suffix_b eq '.pl'; 253 | return $a cmp $b if $suffix_a eq '.pl' && $suffix_b eq '.pl'; 254 | return -1 if $suffix_a eq '' && $suffix_b ne ''; 255 | return 1 if $suffix_a ne '' && $suffix_b eq ''; 256 | return $a cmp $b if $suffix_a eq '' && $suffix_b eq ''; 257 | return $a cmp $b; 258 | } @results; 259 | $target = $results[0]; 260 | } 261 | else { 262 | my @paths = get_bin_paths($version, $prog); 263 | for my $path (@paths) { 264 | if (-e $path) { 265 | $target = $path; 266 | last; 267 | } 268 | } 269 | } 270 | } 271 | 272 | if (!$target) { 273 | say STDERR "$brew_name: $prog: command not found"; 274 | if(whence($prog)) { 275 | say STDERR <