perlbrew

Auto set perlbrew with bash and git

I’ve started using the lib feature of perlbrew with all of my perl projects. I create a local::lib for each project and switch using ‘perlbrew use’ accordingly.

The following bash script aliases 'cd’ so that when I switch to a git directory the correct perlbrew lib is automatically set.

To accomplish this I set two custom git config parameters:

mydata.perlbrew.perl
mydata.perlbrew.lib

If I want to use perl version 5.14.2 and a local::lib called 'myproject’

git config mydata.perlbrew.perl perl-5.14.2
git config mydata.perlbrew.lib myproject

I use git config mostly out of laziness. The git command already knows how to figure out when you are in a repository at any directory level so I save having to write that code in shell script.

Gist

function cd_func () {
  builtin cd "$@"
  wanted_perl=$(git config mydata.perlbrew.perl 2>/dev/null)

  if ([ -n "$wanted_perl" ]); then
    wanted_lib=$(git config mydata.perlbrew.lib 2>/dev/null)

    perlbrew_cmd="perlbrew use $wanted_perl@$wanted_lib"

    if ([ -n "$wanted_lib" ]); then
      if ([ -n "$PERLBREW_PERL" ] && [ -n "$PERLBREW_LIB" ]); then
        if [ $PERLBREW_PERL != $wanted_perl ] || [ $PERLBREW_LIB != $wanted_lib ]; then
          ${perlbrew_cmd}
        fi
      else
        ${perlbrew_cmd}
      fi
    fi
  elif ([ -n "$PERLBREW_PERL" ] && [ -n "$PERLBREW_LIB" ]); then
    perlbrew_cmd="perlbrew use perl-5.14.2@myperl"
    ${perlbrew_cmd}
  fi  

}

alias cd=cd_func

You will notice that I have a base lib called 'myperl’ that I switch to whenever I am not working in a Git repository. It will only set this value if PERLBREW_PERL and PERLBREW_LIB are not already set. This allows me to use perlbrew freely outside of git repositories.

Perlbrew を利用したプロダクション環境構築
  • 2011.11.05 carton追記
  • 2011.12.07 スクリプトに追加するPATHの記述を修正
  • 2011.12.09 perlbrewのURL変更

Tips: 複数の案件が同居する “開発環境” では perlbrew lib 使うとライブラリパスを簡単に切り替えることができて便利だぞ!

  1. アプリケーションユーザーを作成

    # useradd app-user
    
    # cat /etc/passwd
    ...
    app-user:x:1001:1001::/home/app-user:/bin/bash
    
    # cat /etc/shadow
    ...
    app-user:!!:15266:0:99999:7:::
    

    これで次のような状態のユーザーが作成されるぞ。

    • パスワード未設定
    • /home/app-user
    • /bin/bash
    • 一般ユーザーの su は NG
    • SSH, Telnet でのログインも NG
  2. app-user で環境構築

    ユーザー app-user に変更して…

    # su - app-user
    

    perlbrew をインストールしてから…

    $ curl -kL http://install.perlbrew.pl | bash
    $ echo 'source ~/perl5/perlbrew/etc/bashrc' >> ~/.bashrc
    $ source ~/.bashrc
    $ perlbrew list
    ...
    

    cpanm をインストールして…

    $ perlbrew install-cpanm
    

    perl をインストールして有効化。

    $ perlbrew available
    ...
    
    $ perlbrew install perl-<VERSION>
    ...
    
    $ perlbrew switch perl-><VERSION>
    
  3. アプリケーションのデプロイ

    でぷろい!cpanm –installdepsで依存モジュールをインストールするんだ!

    carton

    開発環境でcartonを使ってモジュール管理していた場合はアプリケーションパッケージにcarton.lockがあるはずだ。 この場合はcarton installで依存モジュールをインストールできるぞ!

    cartonを使うと開発環境と同じバージョンのモジュールがインストールされる。 「コードは一切変更がないのにデプロイしたらエラーが出る」なんてことが少なくなると思うぞ。開発環境と本番環境で容易にモジュールのバージョンを合わせることができるんだ。

    carton install するとデフォルトでは local/ にモジュールがインストールされる。 perlスクリプト内で use lib するか perl -Ilocal/lib などでライブラリパスを通すのを忘れちゃダメだぞ!

    cartonはまだ開発中ということで今後に期待だ。はやくcarton bundleを使いたいぞ。

  4. アプリケーション実行スクリプト(daemontools の run スクリプトなど)

    最後に実行スクリプトに perlbrew でインストールした perl のパスを追加して完了だ。

    PATHに追加するperlbrewと利用するperlのパスは $PERLBREW_ROOT, $PERLBREW_PERL で確認するんだ!

    ちなみに現在perlbrewを使って使用しているperlのバスは $PERLBREW_ROOT/perls/$PERLBREW_PERL/bin だぞ!

    ...
    export PATH=/home/app-user/perl5/perlbrew/bin:/home/app-user/perl5/perlbrew/perls/perl-5.14.1/bin:${PATH}
    exec setuidgid app-user ...
    
Cygwin paths and perlbrew

I’ve been messing around with perlbrew lately and decided to give it a whirl on Cygwin. Right off the bat I ran into a problem with my $PATH.

PATH=/usr/local/bin:/usr/bin:/usr/local/bin:/usr/bin:/cygdrive/c/Program Files/Common Files/Microsoft Shared/Windows Live:/cygdrive/c/Program Files (x86)/Common Files/Microsoft Shared/Windows Live:/cygdrive/c/Program Files (x86)/AMD APP/bin/x86_64:/cygdrive/c/Program Files (x86)/AMD APP/bin/x86:/cygdrive/c/Windows/system32:/cygdrive/c/Windows:/cygdrive/c/Windows/System32/Wbem:/cygdrive/c/Windows/System32/WindowsPowerShell/v1.0:/cygdrive/c/Program Files/Intel/WiFi/bin:/cygdrive/c/Program Files/Common Files/Intel/WirelessCommon:/cygdrive/c/Program Files (x86)/ATI Technologies/ATI.ACE/Core-Static:/cygdrive/c/Program Files (x86)/Intel/Services/IPT:/cygdrive/c/Program Files (x86)/Symantec/VIP Access Client:/cygdrive/c/Program Files (x86)/Sony/VAIO Startup Setting Tool:/cygdrive/c/Program Files (x86)/Common Files/Roxio Shared/10.0/DLLShared:/cygdrive/c/Program Files (x86)/Common Files/Roxio Shared/DLLShared:/cygdrive/c/Program Files (x86)/Windows Live/Shared

Spaces everywhere, which the perl installer did not take kindly to.

I had two options at this point:

  1. Set $PATH to only include what was needed to install and be done with it
  2. Spend a good chunk of time Googling how to convert the paths to something perl wouldn’t complain about.

I of course, took option 2.

I wanted my default paths to work with whatever I needed to do. $PATH is given to Cygwin by Windows, so I’d like to keep it that way for consistency. The necessity of doing this can be debated.

I needed:

/cygdrive/c/Program Files/Common Files/Microsoft Shared/Windows Live

to become:

/cygdrive/c/PROGRA~1/COMMON~1/MICROS~1/WINDOW~1

cygpath

I found cygpath and thought it would be all I needed.

I was wrong.

The command has options to display unix style paths, windows style paths and short paths, but it does not allow you to combine the short style with the unix style.

Unix Style Path:

/cygdrive/c/Program Files/Common Files/Microsoft Shared/Windows Live

Mixed Style Path:

$ cygpath -m "/cygdrive/c/Program Files/Common Files/Microsoft Shared/Windows Live"
C:/Program Files/Common Files/Microsoft Shared/Windows Live

Short Style Path:

$ cygpath -m -s "/cygdrive/c/Program Files/Common Files/Microsoft Shared/Windows Live"
C:/PROGRA~1/COMMON~1/MICROS~1/WINDOW~1

Fixing $PATH

I would need to create a shell script to combine the mixed and short style paths into something I could use.

Gist

# First we need to split $PATH
# on ':' and put each line into
# an array

OLD_IFS=$IFS
IFS=$'\n'
paths=(`echo $PATH | tr -s ":" "\n"`)
IFS=$OLD_IFS



# The paths supplied from Windows
# may not actually exist. These
# paths will cause cygpath to error
# so we will skip them and only
# process actual directories.

valid_paths=();
for path in "${paths[@]}"; do
  if [ -d "${path}" ]; then

    # Save the cygdrive we are parsing
    cygdrive_save=$(echo $path | grep -oEi "/cygdrive/[a-z]+")

    if [[ $cygdrive_save ]]; then

      # Get the short version of the path from cygpath
      # We can't have ':' in our path so only grab the
      # path following C: (or other drive letter)
      no_space_path=$(cygpath -a -m -s "${path}" | cut -d : -f 2);


      # Use the saved cygdrive from above and
      # prepend it to the short path
      valid_paths=("${valid_paths[@]}" "${cygdrive_save}${no_space_path}")

    else
      valid_paths=("${valid_paths[@]}" $path)
    fi
  fi
done



# Join the valid_paths array with ':'
# and set $PATH

OLD_IFS=$IFS
IFS=":"
PATH="${valid_paths[*]}"
IFS=$OLD_IFS

Add to .bash_profile and now my $PATH looks like this:

$ printenv PATH
/home/Kevin/perl5/perlbrew/bin:/usr/local/bin:/usr/bin:/usr/local/bin:/usr/bin:/cygdrive/c/PROGRA~1/COMMON~1/MICROS~1/WINDOW~1:/cygdrive/c/PROGRA~2/COMMON~1/MICROS~1/WINDOW~1:/cygdrive/c/PROGRA~2/AMDAPP~1/bin/x86_64:/cygdrive/c/Windows/system32:/cygdrive/c/Windows:/cygdrive/c/Windows/System32/Wbem:/cygdrive/c/Windows/System32/WINDOW~1/v1.0:/cygdrive/c/PROGRA~1/Intel/WiFi/bin:/cygdrive/c/PROGRA~1/COMMON~1/Intel/WIRELE~1:/cygdrive/c/PROGRA~2/ATITEC~1/ATI.ACE/CORE-S~1:/cygdrive/c/PROGRA~2/Intel/Services/IPT:/cygdrive/c/PROGRA~2/Symantec/VIPACC~1:/cygdrive/c/PROGRA~2/Sony/VAIOST~1:/cygdrive/c/PROGRA~2/COMMON~1/ROXIOS~1/10.0/DLLSHA~1:/cygdrive/c/PROGRA~2/COMMON~1/ROXIOS~1/DLLSHA~1:/cygdrive/c/PROGRA~2/WIC4A1~1/Shared

Now the Perl install (and anything else that is space sensitive) works:

$ perlbrew list
perl-5.14.2

$ perlbrew exec perl -v
perl-5.14.2
==========

This is perl 5, version 14, subversion 2 (v5.14.2) built for cygwin-thread-multi-64int

Copyright 1987-2011, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

Shell scripting is not my strong suit. I’m guessing there is a better and possibly more efficient solution to this problem. I welcome any suggestions to clean it up.

perlbrewで入れたPerlをcronで実行するとき、いくつか設定する必要があります。
まず、次のようなシェルスクリプトを用意します。

env.sh

#!/bin/sh
export HOME=/home/uchiyama
source ~/perl5/perlbrew/etc/bashrc
perlbrew use perl-5.16.2

exec "$@"

そして、crontabの設定で次のような感じで記述します。

* * * * * /home/uchiyama/env.sh perl /home/uchiyama/hoge.pl > /tmp/hoge 2>&1

以上のようにに設定することで、perlbrewでインストールされたPerlが使われるようになります。
普通に実行してしまうと、システムに標準で入っているPerlの方で実行されてしまうので注意して下さい。

perlbrew 를 이용하여 다양한 버전의 Perl을 설치해보자!

CentOS 5.6 에 기본으로 설치되어 있는 Perl 의 버전은 5.8.8 이다. 버전이 낮아도 한참 낮다. 요즘 다른 OS에는 5.10을 기본으로 사용하도록 되어 있으며, 최신 버전은 5.12 이다. 거기에 벌써 5.14 RC 버전까지 나온 상태이다. 이런 구닥다리 버전을 벗어나서 그나마 남들도 쓰는 버전을 쓰려면, 별도로 Perl 을 설치해야 되는데… 직접 소스 파일을 다운로드하여 컴파일/설치 하는 것도 방법이겠지만… 더욱 유연하고 확장성 있는 방법을 소개하고자 한다. 지난번 pythonbrew 를 이용해서 여러 버전의 python 을 설치하는 법을 알려드렸는데, 이것의 원조인 perlbrew 를 이용하면 우리가 원하는 것을 할 수 있다.

먼저 perlbrew를 설치한다.

$ curl -Lk http://xrl.us/perlbrewinstall | bash

설치를 완료하면 ~/perl5/perlbrew 가 생성된다. 그리고, 여기에 모든 파일들이 들어있다.

.bashrc 에 다음을 추가한다.

source ~/perl5/perlbrew/etc/bashrc

Perl 5.12.3 설치해보기

$ . .bashrc
$ perlbrew install --force perl-5.12.3
Perl 5.12.3 사용해보기
$ perlbrew use perl-5.12.3
$ perl -v

This is perl 5, version 12, subversion 3 (v5.12.3) built for i686-linux

Copyright 1987-2010, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

Perl 시스템에 설치된 원래 버전 사용하기

$ perlbrew off

perlbrew is switched off. Please exit this shell and start a new one to make it effective.
To immediately make it effective, run this line in this terminal:

    exec /bin/bash

$ perl -v

This is perl, v5.8.8 built for i386-linux-thread-multi

Copyright 1987-2006, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

perl-5.12.3 을 default Perl 로 설정하기

$ perlbrew switch perl-5.12.3
Switched to perl-5.12.3