いけむランド

はてダからやってきました

非アーカイブからパッケージにする時の HACK

実は 0.10.9 現在の cygport では想定するソースはアーカイブである必要があるという制限がある。具体的に言うと、hoge.c みたいな単体のソーステキストからパッケージにするには少し HACK が必要となる。


cygport のサブコマンド prep は内部の unpack() という関数で指定されたファイルの拡張子を基に解凍するコマンドを決定している。

# unpacks archives
unpack() {
	local unpack_cmd;
	local unpack_file_path;
	local unpack_file_name;
	local unpack_out;

	for unpack_file_path
	do
		unpack_file_name=${unpack_file_path##*/};

		if [ ! -f ${unpack_file_path} ]
		then
			error "Cannot find source package ${unpack_file_name}";
		fi

		# determine correct source decompression command
		case ${unpack_file_path} in
			*.asc|*.md5|*.sig|*.sign)  continue ;;
			*.tar.bz2|*.tbz2|*.tar.bz|*.tbz) unpack_cmd="tar jxf" ;;
			*.tar.gz|*.tgz|*.tar.Z) unpack_cmd="tar zxf" ;;
			*.tar.lzma|*.tar.xz)
				check_prog_req xz
				unpack_cmd="tar --xz -xf"
				;;
			*.tar.lz)
				check_prog_req lzip
				unpack_cmd="tar -I lzip -xf"
				;;
			*.tar.lzo)
				check_prog_req lzop
				unpack_cmd="tar --lzop -xf"
				;;
			*.tar)  unpack_cmd="tar xf"  ;;
			*.bz2)
				unpack_cmd="bunzip2 -c";
				unpack_out="${unpack_file_name%.bz2}";
				;;
			*.cpio.gz)
				if check_prog bsdtar
				then
					unpack_cmd="bsdtar zxf";
				else
					unpack_cmd="__cpio_gz_extract";
				fi
				;;
			*.gz)
				unpack_cmd="gunzip -c";
				unpack_out="${unpack_file_name%.gz}";
				;;
			*.gem)
				unpack_cmd="__gem_extract";
				;;
			*.rar)
				check_prog_req unrar;
				unpack_cmd="unrar x -inul";
				;;
			*.src.rpm)
				unpack_cmd="__srpm_extract";
				;;
			*.shar)
				check_prog_req unshar;
				unpack_cmd="unshar";
				;;
			*.xar)
				check_prog_req xar;
				unpack_cmd="xar -xf";
				;;
			*.zip|*.ZIP)
				check_prog_req unzip;
				unpack_cmd="unzip -oq";
				;;
			*.7z)
				if check_prog 7zr
				then
					unpack_cmd="7zr x";
				elif check_prog 7za
				then
					unpack_cmd="7za x";
				else
					error "p7zip is required to unpack this source package";
				fi
				;;
			*) unpack_cmd="cp -t ${SRC_DIR}" ;;
		esac

		__step "Unpacking source ${unpack_file_name}";

		if defined unpack_out
		then
			if ! ${unpack_cmd} ${unpack_file_path} > ${unpack_out}
			then
				error "${unpack_cmd} ${unpack_file_name} failed";
			fi
		else
			if ! ${unpack_cmd} ${unpack_file_path}
			then
				error "${unpack_cmd} ${unpack_file_name} failed";
			fi
		fi
	done
}


アーカイブの場合は case 文の最後の条件にマッチすることになるため、cp でコピーをする処理に入るのだが、SRC_DIR はこのファイルを処理する前に別のアーカイブアーカイブと同じ名前のルートディレクトリを含んでいて、それが既に展開されていることを仮定しているため、そのままではコピー先のディレクトリがないと言われてエラーになってしまう。

ちなみにアーカイブであっても、そのアーカイブアーカイブ名と同じルートディレクトリを含まない場合は unpack() の後の cd で移動先のディレクトリがないと言われて、同様にエラーとなってしまう。

ここでコピー先となる SRC_DIR の初期値はアーカイブの名前から拡張子を覗いたものであるが、アーカイブの名前とアーカイブ内のルートディレクトリの名前が異なる場合はこの変数を .cygport ファイル内で再定義する必要がある。

アーカイブ内にルートディレクトリがある場合は SRC_DIR の再定義で対処できるが、ルートディレクトリがない場合やそもそもソーステキストのみしか配布されていない場合はルートディレクトリを用意する必要がある。
unpack() を見る限り外部から hook するのは難しいと思ったが、unpack_cmd を function にすることで対処できることに気づいた。

unzip()
{
        /usr/bin/unzip $* -d ${SRC_DIR}
}
cp()
{
        mkdir -p ${SRC_DIR}
        /usr/bin/cp $*
}


上記のようなコマンド名と同じ function を定義することで行儀の良くないアーカイブからでもパッケージにすることができる。*1

まとめ

if (ソースがアーカイブ) {
        if (アーカイブがルートディレクトリを含む) {
                そのままで良い
        } else {
                ダミーのルートディレクトリをつくってそこにアーカイブを展開させる
        }
} else { // ソーステキストのみ
        ダミーのルートディレクトリをつくってそこにコピーする
}


【追記】tar を再定義する場合は prep の場合のみ動作するようにする必要がある。

tar()
{
        case ${argv[${arg_n}]} in
                prep|unpack)
                        mkdir -p ${P}
                        cd ${P}
                ;;
        esac
        /usr/bin/tar $*	
}

*1:以前はルートディレクトリを '.' にすることでお茶を濁していたが、再度 *-src.tar.bz2 からパッケージにする際にパッチをきちんと適用できないという問題があったため、別の方法を考える必要があった。