主题 : Tiny210v2( S5PV210 )平台下开发方式以及管理方式的探讨 复制链接 | 浏览器收藏 | 打印
级别: 骑士
UID: 5844
精华: 9
发帖: 64
金钱: 770 两
威望: 154 点
贡献值: 9 点
综合积分: 308 分
注册时间: 2009-05-11
最后登录: 2019-05-14
楼主  发表于: 2013-01-25 19:54

 Tiny210v2( S5PV210 )平台下开发方式以及管理方式的探讨

管理提醒: 本帖被 xoom 执行加亮操作(2013-01-26)
0. 概要介绍
1. 交叉编译环境
2. 目标运行环境
3. 软件管理方式
4. 软件编译方法和打包方法
5. SPEC说明
6. 自动编译系统
7. 已经完成的rpm包



0. 概要介绍
=========================================================
这篇文章其实主要是想讨论交叉环境下的开发方式以及管理方式,因为没有确认完,所以还没有找到一种非常好的解决方案,所以这也算是抛砖引玉吧。

当作为一个开发团队合作开发整个系统的时候,不得不考虑管理的问题,人和人之间如何协作,工作流程,责任分工,版本管理等等。
开源社区应该有相应的解决方案,比如 OpenSuse 的 Open Build Service( OBS ),因为还没有确认,无法得知其中的细节,有经验的朋友不妨说一说。

作为自己的经验以及思考,总结一下自己的体会吧。


1. 交叉编译环境
=========================================================
通过开发板提供商的光盘,或者是crosstool-ng,很容易得到一个完整的交叉编译工具集。
但是大部分时候,我们的开发板运算能力有限,无法在开发板上直接编译生成整个软件stack,就只能在开发PC上通过交叉编译器生成目标环境的文件。

软件stack做成的过程中,最麻烦的无非就是软件的依赖关系,以及编译过程中的软件依赖。
软件本身的依赖,可以参考 Beyond Linux From Scratch ,或者通过 packages.debian.org / packages.ubuntu.org 直接查看软件本身的依赖关系,这也就是先编译谁,后编译谁的问题。
编译过程中的依赖是指通常在 configure 阶段,configure 查找的编译依赖。由于开发PC通常是Linux系统,configure 很有可能找到PC上的内容,而不是目标环境的内容,因此导致污染。
也可能 configure 阶段需要运行目标环境下的二进制程序。
因此我们希望有一种机制能够在开发PC上能够将开发PC和目标环境进行隔离,也希望有一种在开发PC上运行目标环境程序的能力。

幸运的是,社区中已经有人在解决这个问题了:scratchbox 和 qemu user mode。
scratchbox 通过 chroot 的方式,隔离PC环境和目标环境,并通过 qemu 提供运行目标环境程序的能力。
scratchbox 分为 1 和 2 两个版本。版本1是需要登录到 scratchbox 环境的,就像是通过 chroot 登录到另外一个 rootfs 一样,你在这个环境下工作就像是在目标环境中一样。
并且也可以直接 ./a.out 运行 a.out 这个针对 目标环境的程序,scratchbox 在底层让 qemu 完整了这个工作,让你完全觉察不到。

我使用 scratchbox 的时候,感觉 1版本有2个不足点:难以添加自己的编译工具集;进入scratchbox环境以后,难以在目标环境和开发环境之间共享资源。

因此,scratchbox发展出来了 2版本,又叫做 SBOX2。这个版本最大的特点可以不许要登录到 scratchbox 环境,
比如以前需要登录到scratchbox 环境以后,在 ./configure 的操作,现在直接 sb2 ./configure 就可以了, sb2 这个命令会让 configure 工作在它的沙盒中,就像登录 scratchbox 1 一样。
当然也可以输入sb2 直接登录到 SBOX2 环境中,这个很像 scratchbox 1。

在scratchbox 1 中,交叉工具是通过 scratchbox 1 提供的软件包安装的,想要制作 scratchbox 1 可以使用的交叉工具很困难。
在scratchbox 2 中,交叉工具是自己提供的,在初始化环境的过程中告知 scratchbox 2 的,所以方便了很多。

现在开源社区倾向于使用 scratchbox 2。

在ubuntu 12.04 环境中,通过下面的命令可以直接安装 scratchbox2 和 qemu。
sudo apt-get install scratchbox2 qemu-user

安装完成以后,首先需要建立目标环境。
cd /opt/S5PV210/rootfs
sb2-init -c /usr/bin/qemu-arm S5PV210 /opt/linaro-gcc473-eglibc216-armv7a-neon/bin/arm-linux-gcc

上面的命令建立一个名为 S5PV210 的目标环境, 目标环境的根文件系统位于 /opt/S5PV210/rootfs,
交叉编译工具在 /opt/linaro-gcc473-eglibc216-armv7a-neon/bin, qemu 程序位于 /usr/bin/qemu-arm。

在建立目标环境之前,你需要确保交叉编译工具已经做好,确保最小的根文件系统已经做好。如果想要自己做一个,参考:

利用crosstool-ng创建自己的交叉编译工具链toolchain
http://www.aiothome.net/read.php?tid-27480.html

Tiny210v2(S5PV210)平台下创建基本根文件系统
http://www.aiothome.net/read.php?tid=27762

sb2-init 这个命令会在你的home目录下建立 .scratchbox2 文件,并记录相关信息。同时还会编译一个 libtool。
在 libtool 编译的过程中,可能会出错,你可以修改 /usr/bin/sb2-build-libtool 在 line 47 的 configure 后面添加一个 --host=arm:
47 ./configure --prefix=$HOME/.scratchbox2/$TARGET --build=$(uname -m)-unknown-linux-gnu --host=arm

一切顺利的话, SBOX2 的目标环境就OK了。
可以运行 sb2 命令进入到 S5PV210 这个沙盒中。
zoulz@Seagate:~$ sb2
[SB2 simple S5PV210] zoulz@Seagate ~ $ gcc -v
Using built-in specs.
Reading specs from /usr/share/scratchbox2/modes/simple/gcc-specs
rename spec cpp to old_cpp
COLLECT_GCC=/opt/linaro-gcc473-eglibc216-armv7a-neon/bin/arm-linux-gcc
COLLECT_LTO_WRAPPER=/opt/linaro-gcc473-eglibc216-armv7a-neon/libexec/gcc/arm-unknown-linux-gnueabi/4.7.3/lto-wrapper
Target: arm-unknown-linux-gnueabi
Configured with: /root/CT-NG/.build/src/gcc-linaro-4.7-2012.12/configure --build=i686-build_pc-linux-gnu --host=i686-build_pc-linux-gnu --target=arm-unknown-linux-gnueabi --prefix=/opt/linaro-gcc473-eglibc216-armv7a-neon --with-sysroot=/opt/linaro-gcc473-eglibc216-armv7a-neon/arm-unknown-linux-gnueabi/sysroot --enable-languages=c,c++ --with-arch=armv7-a --with-cpu=cortex-a8 --with-tune=cortex-a8 --with-fpu=neon --with-float=hard --with-pkgversion='crosstool-NG hg+unknown-20130103.094521 - S5PV210' --enable-__cxa_atexit --disable-libmudflap --disable-libgomp --disable-libssp --disable-libquadmath --disable-libquadmath-support --with-gmp=/root/CT-NG/.build/arm-unknown-linux-gnueabi/buildtools --with-mpfr=/root/CT-NG/.build/arm-unknown-linux-gnueabi/buildtools --with-mpc=/root/CT-NG/.build/arm-unknown-linux-gnueabi/buildtools --with-ppl=/root/CT-NG/.build/arm-unknown-linux-gnueabi/buildtools --with-cloog=/root/CT-NG/.build/arm-unknown-linux-gnueabi/buildtools --with-libelf=/root/CT-NG/.build/arm-unknown-linux-gnueabi/buildtools --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++,-Bdynamic -lm -L/root/CT-NG/.build/arm-unknown-linux-gnueabi/buildtools/lib -lpwl' --enable-threads=posix --enable-plugin --disable-multilib --with-local-prefix=/opt/linaro-gcc473-eglibc216-armv7a-neon/arm-unknown-linux-gnueabi/sysroot --enable-c99 --enable-long-long
Thread model: posix
gcc version 4.7.3 20121205 (prerelease) (crosstool-NG hg+unknown-20130103.094521 - S5PV210)
[SB2 simple S5PV210] zoulz@Seagate ~ $

如果使用 sb2 命令登录 S5PV210, 可以发现 / 文件系统和 开发PC 的/ 文件系统几乎是一样的。但是几个重要的目录,比如 /usr /bin /sbin 等,都不能写,这是scratchbox 的保护机制。
如果使用 sb2 -e 命令登录 S5PV210, 可以发现 / 文件系统和 /opt/S5PV210/rootfs 一致。这时候,由于很多命令都没有从 PC 上映射到 S5PV210下,导致很多命令没有。


2. 目标运行环境
=========================================================
目标运行环境其实就是目标根文件系统/opt/S5PV210/rootfs。
开发阶段,我采用网络加载kernel和网络挂载rootfs的方式节省flash烧写时间。
具体方法参考:

Tiny210v2( S5PV210 )平台下: 利用uboot启动远程Kernel(TFTP)以及挂载远程ROOTFS(NFS)
http://www.aiothome.net/read.php?tid-27737.html


3. 软件管理方式
=========================================================
在建立根文件系统以及软件stack过程中,可能需要反反复复编译软件包,因此我们需要将整个过程管理起来,一是可以回溯每一步的修改,另外万一根文件系统损坏或者想要重现,这可以快速实现。
最好的方法就是使用版本管理工具,比如 CVS/SVN/GIT 等管理成果物,包括源代码和二进制文件,以及文档。

另外,目标环境其实也分为2个部分,开发环境 和 运行环境。为了减少目标资源的时候,运行环境通常只包含运行库和必要的工具;而开发环境则包括调试符号,头文件,大有全的工具集等。
很有必要引入包管理系统进行管理,将开发环境和运行环境通过安装不同的包进行区分,也可以让软件更新变得更加容易。
比如 libpng 这个软件包,我将其拆分为 libpng-tool  libpng-runtime  libpng-devel 三个包,开发环境3个都安装,但运行环境只安装 runtime 这个包。

常用的包管理系统主要是 deb 和 rpm 两个系统。我个人喜欢 deb 系统,但是制作 deb 包麻烦一些。制作rpm 包似乎更加容易一些。
并且,meego 系统到 tizen 系统的变化,也从 deb 包管理变更为了 rpm 方式。因此,现在的环境中,采用的是 rpm 方式,版本管理使用的是 SVN。

在/opt/S5PV210/rootfs/usr/src/rpm 目录中,有如下5个目录 BUILD SOURCES RPMS SPECS SRPMS 受SVN 管理:
SOURCES 放源代码包,比如XXX。
RPMS 放rpmbuild工具生成的二进制文件包,会将rpm包放在它下面的armv7l目录中。比如 libpng-devel-1.5.13-1.armv7l.rpm 和 libpng-runtime-1.5.13-1.armv7l.rpm。
SPECS 放rpmbuild 工具依赖的 spec 文件,比如 libpng.spec。
SRPMS 放rpmbuild工具打包的包含spec文件以及源代码包的以rpm结尾的包,比如 libpng-1.5.13-1.src.rpm。
BUILD 目录存放的是 源代码包解压后的目录,编译也在这里,编译过程中生成的中间文件也在这里,比如 libpng-1.5.13/  目录。

对于BUILD这个目录的管理,目前还存在一些问题:
我希望将解压后的源代码也放在这个目录中,通过SVN进行管理,但是rpmbuild 过程中,会将目录全部删除,导致SVN管理信息也删除了,因此无法达到管理的目的。
这个问题现在还没有解决。

由于 rpmbuild 这个工具,输入是以源代码包进行输入的,所以 源代码加压后的目录,只能在另外一个目录进行管理,并增加一个步骤用于 将源代码打包成 tar.gz/bz2/xz 这样的格式。

软件包的变化流程为:
    源代码目录(libpng-1.5.13)
    ---tar--> 源代码包(libpng-1.5.13.tar.bz2)
    ---rpmbuild--> 二进制包(libpng-runtime-1.5.13-1.armv7l.rpm)+二进制源代码包(libpng-1.5.13-1.src.rpm)。
    ---rpm--> 安装二进制包到 /opt/S5PV210/rootfs
    ---mkyaffs2image-mlc--> 做成根文件系统镜像

因为有了工作流程,因此就可以考虑什么样的角色在这个流程中完成哪个部分的工作,以及管理方式。


4. 软件编译方法和打包方法
=========================================================
当最小根文件系统做成的时候,没有rpm系统,ubuntu12.04自带的rpm系统是 rpm4 的,而我自己用的是 rpm5 ,所以有一些冲突。
我希望 scratchbox2 不要去利用 ubuntu 12.04 的rpm 命令,而是使用 scratchbox2 中的 rpm。所以确保 ubuntu 中不要安装 rpm。
我们需要首先给目标开发环境手工生成rpm工具。
然后才使用 rpmbuild 系统生成rpm包,并通过 rpm 命令安装到目标环境中。

以rpm这个包为例子,编译的方法是:

* 运行sb2,进入scratchbox2 环境
* 解压rpm源代码: tar xf /opt/S5PV210/rootfs/usr/src/rpm/SOUECE/rpm-5.1.9.tar.gz -C /tmp
* 进入/tmp/rpm-5.1.9
* 配置:./configure --prefix=/usr --libexecdir=/usr/lib --sysconfdir=/etc --localstatedir=/var --mandir=/usr/share/man  --disable-static --enable-shared
* 编译:make -j 4
* 安装:make  DESTDIR=/opt/S5PV210/rootfs install

如果不想登录到scratchbox2环境,在上面的命令前面直接加一个 sb2 也是一样的:
sb2 ./configure --prefix=/usr --libexecdir=/usr/lib --sysconfdir=/etc --localstatedir=/var --mandir=/usr/share/man  --disable-static --enable-shared
sb2 make -j 4
sb2 make  DESTDIR=/opt/S5PV210/rootfs install

需要注意一下,sb2 环境中,对于 /usr 目录是保护的,无法写入,因此需要虚线救国,通过 DESTDIR 改变安装路径,其实也是安装到我们想安装到的地方。
rpm 好像依赖于其他几个软件包 beecrypt sqlite 什么的,如果安装过程中出现依赖不足,则需要编译这些软件包。
在sb2环境下,几乎所有从源代码make的包都可以使用上面的方法安装。

利用上面的方法虽然可以安装成功,但是无法管理,因此当 rpm 安装完以后,我们需要使用 rpm 包重建整个系统。
在开发PC环境下,修改用户 home 目录中的 .rpmmacros 文件,使其定位 rpm 相关文件在 /opt/S5PV210/rootfs/usr/src/rpm 目录中:
%_topdir   /opt/S5PV210/rootfs/usr/src/rpm
%_unpackaged_files_terminate_build  0

上面的修改完成后,即使在 sb2 环境中,rpmbuild 命令就会从 /opt/S5PV210/rootfs/usr/src/rpm 去查找源代码了,生成的rpm 文件也会放到相对应的位置。
以zlib为例子:
* 将 zlib-1.2.7.tar.bz2 放到 /opt/S5PV210/rootfs/usr/src/rpm/SOURCES 目录中。
* 编写 zlib.spec,将其放到/opt/S5PV210/rootfs/usr/src/rpm/SEPCS 目录中。
* 运行 rpmbuild 命令生成rpm包: rpmbuild -ba /opt/S5PV210/rootfs/usr/src/rpm/SEPCS/zlib.spec

一切顺利的话,就会在 RPMS/armv7l 目录中生成如下两个文件:
zlib-devel-1.2.7-1.armv7l.rpm
zlib-runtime-1.2.7-1.armv7l.rpm

不用退出 sb2 环境中,通过下面的命令安装:(退出sb2 环境的话, ubuntu 12.04 环境中也没有安装 rpm )
rpm -r /opt/S5PV210/rootfs  -ivh XXXXXX.rpm  --nodeps

-r /opt/S5PV210/rootfs 是指 rootfs 的路径
--nodeps 是因为 rpm 依赖有问题,比如它可能会依赖 /sh  /  /usr 这样的符号,这个还没有解决,安装过程中暂时不检测依赖。


5. spec说明
=========================================================
在我的环境中,软件包打包分为两类,工具类和开发库类。
工具类比如 dbus ,拆分的时候,命令通常分为 2 个, dbus  和 dbus-devel 。
开发库类,拆分的时候,通常会分为3个: runtime / tool / devel 。

另外,在目标环境中,大部分文档其实是不许要的,所以在 spec 文件中,把man doc 等删除了。

spec是rpmbuild的基础,其实还是比较简单,下面是 zlib.spec 的一个例子:

Name:           zlib
Provides:       zlib
Version:        1.2.7
Release:        1
Summary:        zlib
License:        GPL
Group:          middleware
Source:         %{name}-%{version}.tar.bz2
BuildRoot:      %{_tmppath}/%{name}-%{version}-build
#BuildRequires:  RRR
%description

%package -n     zlib-runtime
Provides:       zlib-runtime
Summary:        zlib-runtime
Group:          middleware
%description -n zlib-runtime

%package -n     zlib-devel
Provides:       zlib-devel
Summary:        zlib-devel
Group:          middleware
Requires:       zlib-runtime = %{version}
%description -n zlib-devel

%prep
%setup -q

%build
./configure --shared --prefix=%{_prefix}
make

%install
rm -rf %{buildroot}
make  prefix=%{buildroot}/usr install
#delete unused files, doc man etc
rm -rf %{buildroot}/usr/man
rm -rf %{buildroot}/usr/share/man
rm -rf %{buildroot}/usr/doc
rm -rf %{buildroot}/usr/share/doc
rm -rf %{buildroot}/usr/info
rm -rf %{buildroot}/usr/share/info
rm -rf %{buildroot}/usr/share/locale
rm -rf %{buildroot}/usr/share/gtk-doc
find  %{buildroot}  -name "*.la" ! -type d   -exec  rm '{}' ';'
find  %{buildroot}  -name "*.a" ! -type d   -exec  rm '{}' ';'

%post -p /sbin/ldconfig

%postun -p /sbin/ldconfig

%files -n zlib-runtime
%defattr(755,root,root)
%{_prefix}/lib/*.so.*

%files -n zlib-devel
%defattr(755,root,root)
%{_prefix}/lib/*.so
%{_prefix}/lib/pkgconfig/
%{_prefix}/include/

注意的是 %{XXX} 这样的宏,都是 rpm 预先定义好了的。不同系统可能不一样。通常情况下:
%{_prefix} 就是 /usr
%{_tmppath} 就是 /var/tmp
以 zlib 为例, %{buildroot} 就是 /var/tmp/zlib-1.2.7-root

%{buildroot} 方的是 make install 的内容,
但是当在 %files 阶段指定的 文件内容,因为 rpmbuild 会默认在 %{buildroot} 去查找,所以要写成以 /目录样子的。
比如 %{_prefix}/include/ 等同于 /usr/include/ 目录下的所有文件。

当rpm 安装软件包的时候,也会将其安装到 rootfs 的 /usr/include/ 目录下。


6. 自动编译系统
=========================================================
当手工将整个系统编译完成以后,整个系统已经可以使用 rpm 全部重构出来了。
如果需要重新生成整个系统,人工一个一个去完成是一件非常痛苦的事情,并且很无聊。

这时候,可以考虑使用自动编译系统,无人职守,自动完成。
我现在还没有找到符合现状的,不过知道 jhbuild 等工具属于完成类似工作的。

不知到 Open Build Service 是如有这样的工具何做到的。

最好是能够解析出来依赖关系,好象Makefile 一样,可以自动化的处理依赖关系的。



7. 已经完成的rpm包
=========================================================
参考 BLFS 的book,在我的机器上完成了下面几个大的部分部件。
DirectFB / Xorg / GTK / QT / EFL / FFMPEG / MPLAYER / ALSA / PULSEAUDIO / GSTREAMER / BLUEZ 等

原本是打算不是用 X,直接建立一个 基于OpenGL 的 EFL 栈,但是 G3D 的说明文档没有,Samsung没公开?
而光盘中的中的 EGL 和 OGL-ES 又是针对 Android 的。用不上。

即使是想通过 G3D 自己实现 OpenGL 栈都没有办法。炒蛋!
所以就只能构建基于 X 的图形系统了。

编译后的rpm 以及源代码最近打算上传到 SF 上,路径:
https://sourceforge.net/p/blfsfortiny210/code
终于上传完了,失败了好多次。

下面是编译的顺序,一共200多个软件包:
beecrypt
popt
sqlite
rpm

sysroot
busybox

zlib
bzip2
ncurses
libffi
expat
icu
pkg-config
openssl
Python
pcre
libxml2
dbus
glib
atk
intltool
jpeg
jasper
libpng
tiff
giflib
pixman
freetype
fontconfig
lcms
openjpeg
libmng
tslib
directfb
directfb-example
alsa-lib
curl

utils-macros
bigreqsproto
compositeproto
damageproto
dmxproto
dri2proto
fixesproto
fontsproto
glproto
inputproto
kbproto
randrproto
recordproto
renderproto
resourceproto
scrnsaverproto
videoproto
xcmiscproto
xextproto
xf86bigfontproto
xf86dgaproto
xf86driproto
xf86vidmodeproto
xineramaproto
xproto
makedepend
libXau
libXdmcp
libpthread-stubs
xcb-proto
libgpg-error
libgcrypt
libxslt
libxcb
xtrans
libX11
libXext
libFS
libICE
libSM
libXScrnSaver
libXt
libXmu
libXpm
libXaw
libXfixes
libXcomposite
libXrender
libXcursor
libXdamage
libfontenc
libXfont
libXft
libXi
libXinerama
libXrandr
libXres
libXtst
libXv
libXvMC
libXxf86dga
libXxf86vm
libdmx
libpciaccess
libxkbfile
gperf
xcb-util
xbitmaps
bdftopcf
iceauth
luit
mkfontdir
mkfontscale
sessreg
setxkbmap
smproxy
x11perf
xauth
xbacklight
xcmsdb
xcursorgen
xdpyinfo
xev
xgamma
xhost
xinput
xkbcomp
xkbevd
xkbutils
xkill
xlsatoms
xlsclients
xmodmap
xpr
xprop
xrandr
xrdb
xrefresh
xset
xsetroot
xvinfo
xwd
xwininfo
xwud
xcursor-themes
font-util
encodings
font-adobe-100dpi
font-adobe-75dpi
font-adobe-utopia-100dpi
font-adobe-utopia-75dpi
font-adobe-utopia-type1
font-alias
font-arabic-misc
font-bh-100dpi
font-bh-75dpi
font-bh-lucidatypewriter-100dpi
font-bh-lucidatypewriter-75dpi
font-bh-ttf
font-bh-type1
font-bitstream-100dpi
font-bitstream-75dpi
font-bitstream-type1
font-cronyx-cyrillic
font-cursor-misc
font-daewoo-misc
font-dec-misc
font-ibm-type1
font-isas-misc
font-jis-misc
font-micro-misc
font-misc-cyrillic
font-misc-ethiopic
font-misc-meltho
font-misc-misc
font-mutt-misc
font-schumacher-misc
font-screen-cyrillic
font-sony-misc
font-sun-misc
font-winitzki-cyrillic
font-xfree86-type1
libusb
usbutils
pciutils
udev
xkeyboard-config
xorg-server
mtdev
xf86-input-evdev
xf86-input-keyboard
xf86-input-mouse xf86-video-fbdev
printproto
libXp
twm
xterm
xclock
xinit

at-spi2-core
at-spi2-atk
gdk-pixbuf
cairo
harfbuzz
pango
pangox-compat
gtk3
gtk2

libdvdread
libdvdnav
libdvdcss
json-c
libogg
libvorbis
flac
libsndfile
speex
libtool
pulseaudio
libtheora
mpg123
faac
xvidcore
which
libvpx
lame
ffmpeg
MPlayer

fribidi
readline
lua
eina
eet
evas.spec
gstreamer
gst-plugins-base
gst-ffmpeg
evas_generic_loaders
ecore
eio
embryo
edje
efreet
e_dbus
eeze
emotion
libexif
ethumb
elementary
xcb-util-keysyms
enlightenment

libnl
wpa_supplicant

qt

gmp
nettle
libtasn1
gnutls
glib-networking
libsoup
ruby

iptables
connman

libusb-compat
bluez
ofono

dbus-glib
dbus-python
GConf
#gpsd
geoclue

enchant
alsa-utils
dhcp
[ 此帖被happyzlz在2013-01-30 07:58重新编辑 ]
级别: 骑士
UID: 5844
精华: 9
发帖: 64
金钱: 770 两
威望: 154 点
贡献值: 9 点
综合积分: 308 分
注册时间: 2009-05-11
最后登录: 2019-05-14
1楼  发表于: 2013-01-30 07:59
编译后的rpm 以及源代码最近打算上传到 SF 上,路径:
https://sourceforge.net/p/blfsfortiny210/code
终于上传完了,失败了好多次。

获取方式:
git clone git://git.code.sf.net/p/blfsfortiny210/code blfsfortiny210-code
级别: 骑士
UID: 5844
精华: 9
发帖: 64
金钱: 770 两
威望: 154 点
贡献值: 9 点
综合积分: 308 分
注册时间: 2009-05-11
最后登录: 2019-05-14
2楼  发表于: 2013-03-04 12:06

 回 8楼(nonplus) 的帖子

paco 的话,能方便的将安装后的文件,拆分成多个包吗?