[첫화면으로]Perl/Dist-Zilla

마지막으로 [b]

Perl /모듈을 제작하고 버전을 관리하고 CPAN에 업로드할 때 사용하는 프로그램. 모듈 저자가 불필요한 메타정보를 관리하는 일을 최소화하고 모듈 자체에만 집중할 수 있도록 돕는다.

...고 하는데, -_-; 막상 이 Dist::Zilla를 쓰는 법을 또 익히는데 영 알쏭달쏭하다. 게다가 튜토리알이... 시작은 친절하게 시작하는데 조금 읽다보면 갑자기 뭔가 있어야 될 내용이 없고 팍팍 넘어가는 느낌이다.

이번에 GitFancygraph를 만드는데 사용하면서 좌충우돌한 부분을 적어둠.

1. 전역 설정
2. 새 프로젝트 생성
3. 모듈 만들기
4. 테스트
5. 빌드
6. 플러그인들
6.1. 번들 필터링
6.2. 메타 정보 추가
6.3. 의존성 명시
6.4. POD 보강
6.5. 버전 넘버 관리
6.6. 추가 파일 생성
7. 프로파일 설정
8. CPAN에 업로드
8.1. 계정 만들기
8.2. 모듈 이름 짓기
8.3. 업로드
8.4. 자잘한 것들
9. 단점과 대안
10. 참고
11. Comments

1. 전역 설정

dzil setup

setup을 수행하면 몇 가지 이름, 이메일 등 기본 정보를 묻고, 그걸 완료하면 ~/.dzil/config.ini파일에 저장된다.
[%User]
name  = ****
email = ****

[%Rights]
license_class    = Perl_5
copyright_holder = ****

2. 새 프로젝트 생성

dzil new

[gypark@www temp]$ dzil new App::gitfancy
[DZ] making target dir /home/gypark/temp/App-gitfancy
[DZ] writing files to /home/gypark/temp/App-gitfancy
[DZ] dist minted in ./App-gitfancy
[gypark@www temp]$ cd App-gitfancy/
[gypark@www App-gitfancy]$ tree
.
|-- dist.ini
`-- lib
    `-- App
        `-- gitfancy.pm

2 directories, 2 files

파일이 두 개 생기는데, 모듈 이름에 해당하는 .pm파일과, dist.ini 파일이다. pm파일에는 패키지 이름만 덜렁 들어 있고, dist.ini는 다음과 같이 생겼음:
name    = App-gitfancy
author  = **** <****>
license = Perl_5
copyright_holder = ****
copyright_year   = 2012

version = 0.001

[@Basic]

여기서 [@Basic]은 "@Basic" 플러그인 번들을 의미한다. dzil을 써서 모듈 배포본을 제작할 때 이 플러그인 번들에 포함되어 있는 플러그인들을 사용하게 된다.

문제는 어떤 플러그인이 뭘 하는지 명확히 튜토리얼에 적혀 있지 않고, CPAN에 보면 사용자들이 만들어서 올린 플러그인들도 많으며, 그래서 도대체 어떤 모듈이 있고, 나는 그 중 어떤 것이 필요한지를 아는 것 자체가 어렵더라 -_-;

3. 모듈 만들기

GitFancygraph는 사실 별 대단치도 않은 스크립트 하나가 전부인 터라, 이걸 어떻게 모듈로 만들어야 하나 고민했다. 비슷하게 스크립트를 제공하는 모듈 몇 가지를 살펴봤는데, 보통은 모듈 안에 run()등의 함수를 만들고, 스크립트에서는 이 모듈을 use한 후 run(@ARGV)를 호출하는 간단한 구조로 되어 있었다. 그런데 Cpan:App::cpanoutdated를 봤더니 이건 스크립트1 쪽에 구현이 다 되어 있고 모듈 파일2은 $VERSION 정보만 덜렁 들어 있는 형태로 되어 있었다. 그래서 얼씨구나 하고 똑같이 흉내내었음 :-)

lib/App/gitfancy.pm3
package App::gitfancy;
use strict;
use warnings;
# ABSTRACT: shows `git log` with a more readable graph

1;

여기서 # ABSTRACT:.... 부분은, 빌드할 때 자동으로 /PODNAME 항목에 들어간다. 이건 플러그인 얘기에서 다시.

그 다음 bin 디렉토리는 따로 생성한 후 여기에 git-fancy 스크립트를 두고, 역시 POD 부분을 좀 손 봐 주었다. bin/git-fancy4
#!/usr/bin/perl
use strict;
use warnings;

# ABSTRACT: shows `git log` with a more readable graph
# PODNAME: git-fancy

use autodie;
use Getopt::Long;
use Pod::Usage;
...
=pod

=head1 SYNOPSIS

    git-fancy [options] [-- arguments for git-log]
...
=cut

POD는 SYNOPSIS부터 시작.

4. 테스트

모듈 파일에 딱히 들어있는 게 없어서 테스트를 할 것도 없다.

bin/git-fancy 의 경우 이걸 테스트하자면 git 저장소를 만들어서 저장소를 특정한 상태로 만들고 출력이 제대로 나오나 확인해야겠지만... 굳이 git가 미리 깔려 있기를 요구하는 것도 이상하고 하니 일단 패스.

(차후에는 Cpan:IPC::Cmd를 써서 특정 실행 파일이 사용 가능한지 확인하도록 할까 싶음)

그러면 모듈 로딩이 잘 되는지 정도나 확인한다. 다른 모듈들을 참조해서 다음과 같이 만듦

t/00-load.t
use Test::More tests => 1;

BEGIN {
    use_ok( 'App::gitfancy' ) or print "Bail out!\n";
}

테스트를 수행하는 것은 dzil test. 임시 디렉토리를 .build/ 아래 만든 후에 거기에 모듈 배포본을 구성하고 그 안에서 테스트를 한다.
[gypark@www App-gitfancy]$ dzil test
[DZ] building test distribution under .build/jtHOrgJut1
[DZ] beginning to build App-gitfancy
[DZ] guessing dist's main_module is lib/App/gitfancy.pm
[DZ] extracting distribution abstract from lib/App/gitfancy.pm
[DZ] writing App-gitfancy in .build/jtHOrgJut1
Checking if your kit is complete...
Looks good
Writing Makefile for App::gitfancy
Writing MYMETA.yml and MYMETA.json
cp lib/App/gitfancy.pm blib/lib/App/gitfancy.pm
cp bin/git-fancy blib/script/git-fancy
/home/gypark/perl5/perlbrew/perls/perl-5.14.2/bin/perl -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/git-fancy
Manifying blib/man1/git-fancy.1
Manifying blib/man3/App::gitfancy.3
PERL_DL_NONLAZY=1 /home/gypark/perl5/perlbrew/perls/perl-5.14.2/bin/perl
"-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/00-load.t .. ok
All tests successful.
Files=1, Tests=1,  0 wallclock secs ( 0.02 usr  0.01 sys +  0.02 cusr  0.00 csys =  0.05 CPU)
Result: PASS
[DZ] all's well; removing .build/jtHOrgJut1

테스트가 성공하면 이 임시 디렉토리도 삭제된다. 테스트에 실패할 경우는 삭제되지 않고 남아 있는다.

배포본을 완전히 구성한 후 테스트하는 형태라 좀 느리다. prove -lr t로 현재 작업 디렉토리의 파일들을 가지고 곧바로 테스트를 시킬 수도 있다.

5. 빌드

dzil build를 하면 현재 배포본 버전의 tarball과, 압축이 풀린 상태의 디렉토리가 만들어진다.

[gypark@www App-gitfancy]$ dzil build
[DZ] beginning to build App-gitfancy
[DZ] guessing dist's main_module is lib/App/gitfancy.pm
[DZ] extracting distribution abstract from lib/App/gitfancy.pm
[DZ] writing App-gitfancy in App-gitfancy-0.001
[DZ] building archive with Archive::Tar; install Archive::Tar::Wrapper for improved speed
[DZ] writing archive to App-gitfancy-0.001.tar.gz

[gypark@www App-gitfancy]$ ls
App-gitfancy-0.001/  App-gitfancy-0.001.tar.gz  bin/  dist.ini  lib/  t/

[gypark@www App-gitfancy]$ tree App-gitfancy-0.001
App-gitfancy-0.001
|-- LICENSE
|-- MANIFEST
|-- META.yml
|-- Makefile.PL
|-- README
|-- bin
|   `-- git-fancy
|-- dist.ini
|-- lib
|   `-- App
|       `-- gitfancy.pm
`-- t
    `-- 00-load.t

4 directories, 9 files

이 시점에서 여러 플러그인이 동작을 한다.

배포본 디렉토리 안에 들어가면 다음 순으로 테스트 및 설치를 할 수 있다.
perl Makefile.PL
make
make test
make install

6. 플러그인들

앞서 말했듯이 수많은 플러그인들이 각각 무슨 용도고 내가 그 중 무엇이 필요한지 아는 것도 쉽지 않다.

여기선 주인장이 App::gitfancy를 만들며 썼던 dist.ini를 예로 든다5.

6.1. 번들 필터링

;[@Basic]
[@Filter]
-bundle = @Basic
-remove = UploadToCPAN

[FakeRelease]
@Basic 번들을 사용하면서, 그 번들에 포함된 UploadToCPAN 플러그인만 제거한다. dzil release할 때 CPAN에 업로드하는 일이 없도록 하여 실수로 업로드하는 것을 막음
dzil release할 때 CPAN에 업로드하는 시점에, 아무 일도 하지 않고 로그만 출력한다.

6.2. 메타 정보 추가

[MetaResources]
;homepage       =
;bugtracker.web =
repository.web = http://github.com/gypark/App-gitfancy
repository.url = git://github.com/gypark/App-gitfancy.git
repository.type = git
위에 적은 항목들을 META.yml에 추가해준다.
위 경우는 다음과 같이 추가되었음
resources:
  repository: git://github.com/gypark/App-gitfancy.git

6.3. 의존성 명시

[AutoPrereqs]
[Prereqs / RuntimeRequires]
Moose = 0
소스 코드의 use, require, base 등의 키워드를 검색하여 자동으로 필요한 모듈을 찾아준다.
여기에서는 명시적으로 필요한 모듈을 적어준다. = 숫자는 최소 버전. 여기서는 Runtime에만 필요하다고 추가로 적어주고 있음

6.4. POD 보강

[PodWeaver]
모듈 작성자가 POD내용에 관한 부분만 적으면, 이 플러그인이 POD 형식에 맞춰 헤딩이나 리스트 등의 문법을 작성해준다. 그런데 나도 정확히 쓰는 법은 잘 모르겠고, 위에서 봤듯이 # ABSTRACT: 뒤에 적은 내용이 자동으로 NAME 항목으로 변환된다.

;[PodCoverageTests]
[PodSyntaxTests]
위 두 플러그인은 dzil release하는 시점에 POD가 제대로 작성되었는지를 테스트해 준다.

6.5. 버전 넘버 관리

현재 작업하고 있는 모듈의 버전 번호를 지정하는 방법은 여러 가지가 있는데,

여기에서는 마지막 방법을 사용하며, 겸사겸사 다른 플러그인도 들어갔다.
[PkgVersion]
[NextRelease]
[Git::NextVersion]
이 플러그인은 VersionFromModule의 역이랄까, dzil이 설정한 현재 버전 값을 모듈 파일에 자동으로 삽입한다:
{
   $MyModule::VERSION = 0.001;
}
Changes 파일에 {{$NEXT}}라고 적어 두면, 이 자리에 현재 버전 번호를 치환해 넣는다
현재 버전의 수정 내용은 {{$NEXT}} 아래에 적어두면 된다.
build 할 때는 Changes 파일에 이번의 버전 번호와 날짜 시각을 치환해서 배포용 파일을 만든다.
release 가 끝나면 그렇게 수정한 Changes 파일의 내용을 작업 디렉토리의 Changes 파일에 반영한다.

이것이 현재 버전 번호를 설정하는 플러그인인데, git tag 목록을 뽑고, ^v(.+)$ 정규식에 매치되는 태그를 찾아서 그 중 제일 마지막 번호를 추출한 후, 그 숫자를 1증가시켜서 현재 버전으로 설정한다. 예를 들어 v0.002 라는 태그가 있다면 dzil build를 수행하면 현재 버전은 0.003으로 처리된다.

버전관리와 관련해서는, 아예 Cpan:Dist::Zilla::PluginBundle::Git 등의 플러그인 번들을 쓰면 git과 완전히 통합해서 commit 등의 동작도 자동으로 하게 할 수 있나본데 (자세히 알아보지는 않았음) 나는 내가 파악하지 못하는 상태에서 자동으로 뭔가 이루어지는 게 불안해서 여기서는 사용하지 않았음

또 한 가지, keedi님이 내게 주었던 프로파일 설정 파일에서는, Cpan:Dist::Zilla::PluginBundle::GitFlow 번들을 사용하도록 되어 있었는데, 이것은 [git 브랜치 모델]의 내용을 반영한 것이다. 내 경우는 굳이 이렇게까지 브랜치 모델을 세워가며 작업할 만한 거창한 프로젝트가 아닌 터라 생략하였다.

6.6. 추가 파일 생성

;[ReadmeMarkdownFromPod]
[InstallGuide]

빌드할 때 모듈의 설치방법이 적힌 INSTALL 문서를 생성한다.

7. 프로파일 설정

새로운 모듈을 작성할 때마다 위와 같이 dist.ini를 비롯한 설정 파일을 작성하거나, 모듈 파일의 기본 형태를 잡아주는 것이 귀찮다. 이 부분도 미리 설정해 놓고, dzil new할 때마다 자동으로 반영되게 할 수 있다.

.dzil/profiles 디렉토리 아래에, 프로파일 이름으로 디렉토리를 만든다. 여기에서는 standard라는 이름으로 만들었다. 이 디렉토리 아래는 다음과 같이 파일들이 있다6.

$ cd ~/.dzil/profiles/standard/
$ tree
.
|-- Module.pm
|-- plugins.ini
|-- profile.ini
`-- skel
    |-- Changes
    `-- weaver.ini

1 directory, 5 files

[DistINI]
append_file = plugins.ini

[GatherDir::Template]
root             = skel
include_dotfiles = 1

[TemplateModule / :DefaultModuleMaker]
template = Module.pm

package {{$name}};
# ABSTRACT: ...

use Moose;
...

Release history for {{ $dist->name }}

{{ '{{ $NEXT }}' }}
    First version, released on unsuspecting world.
이것은 dzil new 했을 때 이렇게 바뀌어 적용되고:
Release history for App-gitfancy

{{ $NEXT }}
    First version, released on unsuspecting world.
여기서 다시 {{ $NEXT }} 부분은 위에서 얘기한 버전 관리 플러그인이 빌드 시점에 치환한다.

이 프로파일을 사용하려면 dzil new -p standard 모듈이름 이렇게 -p 옵션을 주어 지정하면 된다. 만일 프로파일 이름을 default로 지으면, -p 옵션을 쓰지 않았을 때 이 프로파일이 반영된다.

8. CPAN에 업로드

8.1. 계정 만들기

위의 PAUSE에 관한 글을 읽어보고, [PAUSE]에 가서 "Requset PAUSE account"에 들어가서 계정을 신청한다. 내 경우는 몇시간 만에 나왔다. 계정 발급 안내 메일을 따라서 암호를 변경한다.

8.2. 모듈 이름 짓기

PAUSE 안내문에 따르면 적절한 모듈 이름을 짓기 위해서 뉴스그룹에서 의견도 구하고 하라는데... 요새는 그런 걸 잘 지키지 않는다는 듯 하다. PAUSE에 로그인 해서 보면 "Register Namespace"란 것도 있는데 이게 정확히 무슨 역할인지 모르겠다7. 이걸 해야만 내가 작명한 모듈 이름을 쓸 수 있는 건가 싶었는데 꼭 그런 것도 아닌 듯.

어쨌거나 나는 cpan-outdated 처럼 스크립트만 있는 모듈들을 참고하여 App:: 네임스페이스 쪽을 선택했음. 아마도 새 모듈을 만드는 사람은 이 이름 짓는 과정이 제일 힘들지 않을까 싶다.

8.3. 업로드

PAUSE 사이트 들어가서 Upload 메뉴를 찾아서 할 수도 있겠지만 나는 해 보지 않았다. 내 경우는 dist.ini 파일에서 @Filter 번들과 FakeRelease 플러그인을 사용하게 했던 곳을 삭제하고 @Basic 번들을 쓰도록 복원한 후, dzil release 명령을 써서 한번에 업로드하였다.

8.4. 자잘한 것들

[cpan에서 저자 페이지]로 갔을 때, 우측의 아바타는 https://gravatar.com/ 에 가서 자기PAUSE계정@cpan.org 메일 주소로 등록한 이미지가 나온다.

[metacpan에서 저자 페이지] - 여기는 따로 metacpan에 로그인 한 후(트위터나 github 등의 아이디를 사용하여 로그인 가능) PAUSE아이디와 연동을 해 주고, 그 후 계정 설정에서 이미지나 메일 주소 등의 정보를 수정할 수 있다. PAUSE 아이디와 연동을 하면서 왜 굳이 그런 정보를 다시 설정하게 되어 있는지는 의문.

9. 단점과 대안

Dist::Zilla를 사용하여 생성한 저장소를 github 등에 올릴 경우, 저장소에 Makefile.PL 이나 Build.PL 파일이 없기 때문에 이 저장소를 가지고 곧바로 cpanm 등으로 설치할 수가 없고 꼭 클론한 후 dzil install을 수행하는 형태여야 해서 불편할 때가 있다. 아예 CPAN에도 올리는 모듈이라면 CPAN을 통해 설치하면 되는데, 외부에 공개하지 않고 회사 내부에서 공유하는 저장소에 올리는 경우는 문제가 된다.

또한 아무래도 수많은 플러그인들에 겁을 먹게 되는 면도 있고... 그래서 이런 문제를 극복하려는 모듈이 또 새로 나오고 있나보다.

그리고 단지 Makefile.PL이나 cpanfile만 작업 공간에 유지하여 cpanm을 통한 인스톨을 할 수 있도록 하는 용도로는, 다음 모듈을 쓸 수도 있다. Twitter:keedi님이 순식간에 만들어주셨음.

10. 참고

Twitter:aer0님이 소개해주신 링크들

11. Comments

몇가지 dzil 관련 링크 입니다.
-- aero 2012-3-1 10:50 am

링크들을 위의 참고 섹션으로 옮겼습니다 :-) 감사합니다.
-- Raymundo 2012-3-1 5:51 pm
이름:  
Homepage:
내용:
 

컴퓨터분류

각주:
1. https://metacpan.org/source/TOKUHIROM/App-cpanoutdated-0.20/bin/cpan-outdated
2. https://metacpan.org/source/TOKUHIROM/App-cpanoutdated-0.20/lib/App/cpanoutdated.pm
3. POD를 포함한 전체 코드는 https://github.com/gypark/App-gitfancy/blob/master/lib/App/gitfancy.pm
4. https://github.com/gypark/App-gitfancy/blob/master/bin/git-fancy
5. Twitter:keedi님께 많은 도움 받았음. 감사합니다.
6. Twitter:keedi님에게 통채로 받았음. 감사합니다.
7. 아시는 분 가르쳐주세요~

마지막 편집일: 2015-11-25 2:11 pm (변경사항 [d])
1430 hits | Permalink | 변경내역 보기 [h] | 페이지 소스 보기