LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1161|回复: 11

[玛嘎学姐带你学 OBS] (二) OBS 常用命令:以创建一个包为例

[复制链接]
发表于 2011-12-29 20:10:40 | 显示全部楼层 |阅读模式
─=≡Σ((( つ•̀ω•́)つ,欢迎回来!

之前一篇教给大家怎么注册 openSUSE Open Build Service 用户、安装和初始化环境设定的内容,学姐相信大家已经好熟悉了呢!课后没有做功课的拖去平壤街头哭一场金胖子呢!

今天学姐要给大家讲一讲, OBS 的常用命令。还是啦,主要会在命令行里面来操作,网页操作会顺便提到,然后网页操作的不足也会有讲。当然学姐也不会一下子都丢过去,那样子会反感的啦!主要是通过一个 RPM 程式包在 OBS 上完整的创建过程这样一个例子来教学,当中会略过「spec 文件的编写」这块,因为这是第三篇的重点,默认我们就假定这个 spec 文本是写好的啦。

好啦,开始我们的旅程吧~

  • 进入你的车库


大家还记得上一篇我们是怎么获取我们的车库的吗?

   
  1. osc co home:MargueriteSu
复制代码

   
(如果已撷取,可以不用做,即使做它也会告诉你那个车库已在本地存在) 这种方法可以撷取任何人的车库哦~接着使用
   
  1. cd home:MargueriteSu
复制代码

   
就进入你的私人车库啦!

  • 在 OBS 上注册我们的软件包



这步是 OBS 和普通的本地 RPM 打包最大的不同,因为本地打包是给你自己用的,你不需要告诉谁你在打什么包,而 OBS 需要你知会它。不然一会儿你上传的时候它会很费解的。

有三种方法可以做到:

  • 第一种没什么好讲的啦,看图吧:








其中 title 「标题」和 description 「描述」是不「必需」的,但是写了有什么好处呢,就是如果这个包,发行版间是有命名上的不同的,用户可以通过你的描述知道它可能是别的发行版上的哪个包。因为即使包名不同,描述也很可能是相同的,(如果大家都没有偷懒的话)而标题和描述是可「检索」的,无论是在 YaST 的命令行版本和图形版本, 还是在 webpin 页面 software.opensuse.org: Search Results

Tip 3: 这里的可「检索」是不包括 YaST 的后端 zypper 的。YaST 命令行版本是指的 ncurses 版本, 是 root 环境下面(通过 su 进入)键入 yast 进入的那个类似红白机的界面。

  • 第二种方法是通过上一篇教给大家的 meta 指令:


   
  1. osc meta pkg -e home:MargueriteSu application_name
复制代码

   
然后写入 title 和 description。

这里需要特别提及的一个问题就是 osc 下面,任何的「裸创建」作业都是不会成功的。这是和网页版最大的不同。

那么什么是「裸创建」呢?简单说就是创建空包和空子车库。这里空包和空子车库的意思是说不但它们里面没有任何的文件(有一个 0 字节的文件也叫有文件哦),连它们本身的 meta 元数据也是空的。举个例子:

网页版下面你点「创建包」,然后输入个 Name 点确定,是有个包被创建出来的,虽然它底下什么都没有; 但是 osc 下面使用

   
  1. osc meta pkg -c home:MargueriteSu test
复制代码

   
是不会有任何结果的,它就当你这个包不存在,直到你使用

   
  1. osc meta pkg -e home:MargueriteSu test
复制代码

   
来写 title 和 description 。这样创建子车库也是同理,直到你编辑了子车库的 title \ description \ repositories 等一些上一篇初始化环境提到的内容; 或者在这个子车库下面创建了包。所以其实上一篇最后学姐给大家留的创建子车库的方法是骗你们的啦哈哈!

说了这么多是什么意思呢?

就是这种方法是无论如何要写入 title 和 description 的。太懒? 那么看下一种方法。

  • 第三种方法是使用 osc 的 mkpac 指令:


   
  1. osc mkpac application_name
复制代码

   
这是最优化的创建「包」的方式,因为它会略过学姐也懒得写的 title 和 description ,直接在你的车库下创建一个名字是 application_name 的子文件夹。

而如果你使用的是第二个命令来注册的你的包的话,之后你看到的还是空空的车库,要使用

   
  1. osc up home:MargueriteSu
复制代码

   
更新你的车库,或者直接

   
  1. osc co home:MargueriteSu test
复制代码

   
把服务器上的那个包给撷取回来, 这时你的车库中才会有同名文件夹的。而 mkpac 则不一样, 你虽然在本地车库中看到了包名的文件夹,但是除非你向 OBS 做「提交」作业,不然网路上是没有你这个包存在的。所以第二种和第三种注册方法的实现过程是正好相反的。

这个方法的不足在于,它不能「直接」创建子车库。因为 osc 没有 mkrepo 这样的指令。需要一点点变通:

   
  1. osc mkpac home:MargueriteSu:gimp-2.7 gimp
复制代码

   
这样会在创建包的同时把包所在的子车库一并创建掉。当然,这个子车库在网路上也是暂时不存在的,直到你做了「提交」作业。

但是如果你只是需要一个子车库,那你就只好去 osc meta -e 了。

Tip 4: 除了 meta 等特别依赖路径的指令外,其他的本地指令,如果你省略了车库的话,会默认在你现在的车库中操作。当然这意味着你执行指令的时候必须在一个车库的文件夹中啦!否则会出现 not a working copy 错误哦~

  • RPM 打包操作


注册好了你的包后,现在你的车库中有一个包的同名文件夹。

这个文件夹和你系统上的其他文件夹有什么不同呢?

答案是这个包是 under version control 的,「置于版本管理之下」,至于这究竟是什么意思我们都是小白啦,不需要去深入了解。只需要知道一个 under version control 的文件夹有几种表现:

  • 你 cd 作业时的「自动补全」会失灵


都知道按 tab 键可以自动补全路径的吧,这里不是说

   
  1. cd t
复制代码

   
然后按 tab 等它给你自动补全成

   
  1. cd test
复制代码

   
,而是指你在别的文件夹比如你的 /home 下,想要去到这个目录

   
  1. cd Project/h
复制代码

   
按 tab 会补全为

   
  1. cd Project/home:MargueriteSu
复制代码

   
如果有子车库你继续输入

   
  1. :gim
复制代码

   
按 tab 等它自动补全成

   
  1. cd Project/home:Marguerite:gimp-2.7
复制代码

   
这时如果你的车库中只有一个文件夹的话,直接按 tab 在正常的文件系统中会直接补全那个文件夹,这里就不行了。

原因是置于版本管理系统下的文件夹都有一个隐藏目录 .osc,所以其实它下面是有两个目录的,直接补全就无能为力啦,一定要你输入个首字母什么的。

  • 正常的 CLI (命令行) 文件操作对它是无用的。


如果你在一个已经执行过「提交」操作的文件夹中做 rm -rf 操作。看上去它是已经被清空了,但是实际上下次你提交的时候,它会提示你「网路上的某些文件你本地没有,建议先使用 osc up 作业」。如果你照做的话,(╥﹏╥),该死的它们又回来了。

同理即使你删除了这样的文件夹,又新建了同名的文件夹并在里面放了东西的话,除非你再也别用到网路上这个程式包中的东西或者永远别运行全车库的 osc up,不然你会很悲伤的发现,(╬ ̄皿 ̄)凸,那个文件夹里面的文件都被换掉了。

但是在提交前文件夹还是可以被删除的。还记得前面学姐提过的第二种方法和第三种方法吗?因为我们的包是第三种方法创建的,所以在「提交」前是不在版本控制的。比如你包名字创建错了,(真的会的,因为给有些函数库文件打包,是真的会有在本地调试才发现原来包名后面应该加上版本数字才对的情况)这时后可以自由的创建新包并把旧包文件拷贝过去。

下面,

   
  1. cd test
复制代码


  • 制作源代码包


进入这个程式的打包文件夹,拷贝一些源代码过来

   
  1. cp -r /home/marguerite/Source.tar.gz ./
  2.     // -r 表示如果目标位置「./」即当前文件夹
  3.     //    中如果有同名文件就替换掉。
复制代码


这里要说一下,给 openSUSE 打包,源代码最好使用 tar.bz2 后缀的压缩方式,不然即使其他都没错,最后的 log 日志文件也会给你 warning 警告说「大于 1mb 的文件请用 tar.bz2 压缩」。所以

   
  1. tar -xzvf *.tar.gz
  2.     // tar 是 linux 的压缩与解压缩作业,「x」表示
  3.     // 是解压操作,「z」表示文件后缀是「.gz」,
  4.     // 「v」表示显示详细处理过程,「f」表示保存到
  5.     // 本地。更多看「--help」。
复制代码


解压,然后

   
  1. rm -rf *.gz
  2.     // rm 是删除作业, 「r」表示递归操作,删除
  3.     // 子文件夹和里面的文件。「f」表示强制。
复制代码


删除掉老的压缩包,再用

  
  1. tar -cjvf Source.tar.bz2 Source/
  2.     // 「c」表示压缩,「j」表示压缩包后缀「.bz2」。
复制代码


就产生了打包要用的源代码包。

Tip 5: 这时真的不要手贱去 rm -rf 掉刚才解压出来的 Source/ 文件夹, 「rm -rf」这个「成也萧何败也萧何」的指令从推特过来的人都知道,有个学名叫做「人生一课」,意思就是无论什么时候用都会给你上一课,慎之慎之,谁知道你是不是马上就要看里面的文本文档呢。

  • 编写 spec 文本


本篇就省略啦, 我们假设它已经是预先做好了的啦。

可选:

  • 本地编译测试


除非你的 spec 文本超级简单的,就是传说中的 「mkdir & cp -r」**做的,不要任何编译过程的,否则都建议做一下这个过程。即使是**弟子建议也来过一遍这八大铜人阵,因为有些超普遍的 spec 文本错误任何人都逃不过的,比如最终生成的待打包二进制文件的目录中,谁都不能确定是否应该去「拥有」一个文件夹,直到它给你报错通不过编译; 盲目的「拥有」就变成了「占有」了,不知道你们在现实中这么做允许否,但在 RPM 的哲学里是不美的。(超讨厌谈程式哲学、发行版哲学这些东西,但昨天下午一个 arch 党在 linuxtoy 的上篇教学里传教了一下午,那感觉真的好棒。)

使用 osc 来本地编译超棒的。因为它相当于在你的本地系统上开了沙盒哦。

什么是沙盒呢? 沙盒就是在你系统上某个程式的「租界」啦,在这个租界里做任何事都和系统没什么关系的。举个例子,你的系统可能已经更新到 Factory 了,但是你本地编译测试还是可以打 12.1 的包。

但本地编译不是没有条件的,它需要使用你的 /var/tmp 大约 1~1.5GB 左右的空间和一些 CPU 资源。

下面学姐会教大家一些如何节省这些资源的 Tips。

Tip 6:如何节省本地编译所需要的空间。
  • 不缓存编译依赖包


osc 本地编译的时候会下载一些编译依赖包,其中有 108 个,(一百单八将)被称作最小编译环境。它们是即使你不明确写在 spec 文本里也可以用的依赖,比如 gcc,make,automake 这些几乎所有的「编译」都会依赖到的包。其他的就看你的 spec 文本是怎么写的了,比如你写的依赖有 texlive,那么你可能要下载接近 1G 的文件。这太不可以接受了。

本地缓存的依赖在 **/var/tmp/osbuild-packagecache**, 里面是按照 repository/arch 来分类的,这和 **/var/cache/zypp/packages/** 里面好像。实际上就是一回事。所以你可以先「人生一课」掉前者里面对应名字源的对应arch, 然后使用

   
  1. sudo mkdir -pv /var/tmp/osbuild-packagecache/openSUSE:12.1/standard/x86_64
  2.     sudo ln -s /var/cache/zypp/packages/download.opensuse.org-oss/suse/x86_64 /var/tmp/osbuild-packagecache/openSUSE:12.1/standard/x86_64
复制代码


来做个符号链接。默认是没什么用的,因为新的 zypper 改了以前缓存 rpm 包到本地的习惯,所以要先去 /etc/zypp/repos.d/ 里面,把你要缓存 rpm 的源的 .repo 文件里的 keepackages 改成 1.

这里讲的是方法,因为无论看上去还是实际上你都没有省掉那 1GB 的空间嘛!(╬ ̄皿 ̄)凸

但如果你有之前用过没扔的 ISO,那就真的省了,使用

   
  1. sudo mkdir /media/openSUSE_12.1/
  2.     sudo mount openSUSE_12.1.iso /media/openSUSE_12.1/ -o loop
复制代码

   
挂载上。开机自动挂载的方式网路上有好多的。

这种优化的限制有:

  • 没有真正的省空间

只是免去你同一个包下载两遍的麻烦,因为一般你打包的东西你自己最终都是要从源里给自己装上的。而「真正」省空间的方法,不适用 Factory,因为工厂版没有 ISO。

  • 不适用 /tmpfs 党。

有些人的/var/tmp 可能是完全在内存上的。你怎么折腾再开机都是会清空,除非你再把 /var/cache/zypp/packages 符号链接到硬盘上 (๑´ω`๑)♡


  • 强制单线程编译


这个本来应该写到下一篇去,但是写在这里印象会深刻一些,那就是 spec 的编译环节 %build, 如果是提交到 OBS 服务器去编译的话,建议的是:

   
  1. make %{?_smp_mflags}
复制代码


这样会多核心多线程的来编译。但是本地这么干就有点紧张了。一般会给你开双线程,除非你本身就是单核的。所以先把它替换成

   
  1. make -j1
复制代码

   
来做,强制单线程编译,时间会长一些,但是你的 CPU 使用率会下降一些。因为一般本地编译不是因为你要用,而是等着除虫,等待打包抛错的时候一般会刷个推特、上个网什么的,卡着那就太不爽了。

好了,完成这些「优化」之后,让我们真正开始本地编译测试吧。

   
  1. osc build openSUSE_12.1 x86_64 test.spec --local --clean
复制代码

   
命令很好理解。版本和架构。「--clean」表示初始化最小编译环境,如果你已经这么做过一次了,排了一些错后重新测试,如果不是改了 spec 文本中的 Requires 依赖的话,可以使用 「--noinit」这个参数,不初始化最小编译环境直接开编,会省大把的时间。

不知道 deb 可不可以这样格式的写, 比如:

   
  1. osc build xUbuntu_11.10 i386 test.dsc --local --clean
复制代码

   
还请那位「我用 Ubuntu 上传 OBS 我会告你吗」同学过来扫盲一下,学姐真的是不知道。

具体排错的过程放在后面来讲,这里只需要知道使用

   
  1. osc buildlog openSUSE_12.1 x86_64
复制代码

   
可以查看编译的日志输出,这个文件是/var/tmp/buildroot/.buildlog, 而
   
   
  1. osc buildlogtail openSUSE_12.1 x86_64
复制代码

   
可以看最后一行,通常这是导致你编译中断的出错原因。

上面两个命令都有远程版本 remotebuildlog / remotebuildlogtail,即可以查看 OBS 上的打包日志,实际上这两个命令的本身不缩写是 osc localbuildlog。

敢问还能再懒点吗?

能,请用:

  1. osc bl/blt/rbl/rblt/lbl/lblt (bl 是多么高尚的缩写哇!(๑´ω`๑)♡)
复制代码


现在我们假设本地测试已经成功了。

  • 上传到 OBS


本地测试可以解决你大部分的打包问题,下面解决的是分发问题。

你是不可能分享 specfile 给人家的,也不太可能用网盘或者 ftp 分享 RPM 包给人家。不要笑,这就是古时候的方式。学姐前面还想这么分享一个游戏给 sh 家族的两位神秘 linux 客服:格子衬衫的 @shellex 和碎花裙纸的 @shellexy ,结果被劝阻了,因为人家是 deb。 (╬ ̄皿 ̄)凸

所以要上传你的 程式工作目录给 OBS,好让它在服务器那边开工帮你完成分发难题。这也是之前为什么要注册程式给它的原因。

如果你的 specfile 是抄的别人的,而你又是「无视warning」的序员,那么强烈要求你执行

   
  1. osc service localrun format_spec_file
复制代码

   
这至少能把版权头部给你加上,把你文本中的依赖按字母顺序重排列,修复一下语法错误。实际上这是你拿到任何人的 spec 文本第一件应该做的事情。

使用

   
  1. osc vc
复制代码

   
会打开一个 Changlog,你要写一下你打包过程中具体做的事情,比如 切分了包啦,升级了版本啦什么的。可以写中文哦,但我只看过神秘打包侠 「逍遥游」在他「职业生涯」的早期这么做过。后面不知道是学新东方了还是知道害臊了(๑´ω`๑)♡。

接着把你工作目录下的 spec 文本,源代码包,可能有的补丁,.desktop 文件,Changelog 加入到版本控制。(只有被 osc 版本控制的文件才可以被提交哦)

   
  1. osc add *.spec
  2.     osc add *.change
  3.     osc add *.tar.bz2
  4.     ...
复制代码

   
这个时候你才可以去「人生一课」掉之前解压的源代码目录,因为移除太早你写 spec 文本的时候还要去里面查阅 INSTALL、REAME、COPYING 等信息的。

使用

   
  1. osc status
复制代码

   
去看哪些文件在版本控制下,要是出现 (A) test.spec 表示它在。(但是返回空白不代表它不在哦!真正的不在是(!) test.spec ——文件不存在;(?) test.spec ——文件存在但不在版本控制下)使用

   
  1. osc ci
复制代码

   
来上传到 OBS。这是会打开一个版本变动说明给你看,看下头部有哪些文件将要被上传,以免自己少添加了文件到版本控制就好,然后「:wq」保存。这时:

  • 如果你没有移除源代码解压目录的话,会提示你「一个置于版本控制的文件夹下有没有置于版本控制的文件,这些文件将不会被上传」,这是一句废话,我们也没有想要上传它,直接「c」继续就好。
  • 如果你前面没有执行过 osc vc 来写 Changelog 的话,它会提示你「no log file found」,如果确实是你想这么做的话,直接「c」就好。


过了一会,你的 程式工作目录 就上传到 OBS 了。OBS 的设置是只要有 spec 文本就会开始编译,没有就会抛你一个「broken」错误。

Tip 7: 这里还是有点技巧的,就是源代码不用上传,因为你也是网上下载的,直接用 OBS 的「service」去下就好,这算是高级操作了,后面文章会专门讲,这里先默认全部上传好了。


  • 远端控制



一般如果 本地编译测试 做的好的话,除非服务器犯贱,不然这个时候你就可以去干别的了,然后上网的时候顺路查看一下网路版等成功状态就好。但是远端控制有时候还是蛮有必要的。

   
  1. osc results
复制代码

   
直接告诉你服务器端你的包成功还是失败

  
  1. osc results home:MargueriteSu test
复制代码

   
可以查看任何人的任何包。 如果状态是失败,记得用前面教过的

   
  1. osc rbl home:MargueriteSu test openSUSE_12.1 x86_64
复制代码

   
看打包日志。

  • 我不想要一个包/包里的某个文件了怎么办?


除了用网页版去删除之外,(这里就不截图了,因为你有帐号了就会自己看到)还可以使用:

   
  1. osc delete Source.tar.bz2
复制代码

   
这是本地删除某个文件,在下次 osc ci 生效。

   
  1. osc rdelete home:MargueriteSu test
复制代码

   
这是完全的在服务器上毁掉一个包。

   
  1. osc rremove home:MargueriteSu test Source.tar.bz2
复制代码

   
这是在服务器上毁掉一个包中的某个文件。注意 delete 指令 是不作用于远端的,但是如果配合 osc up 可以本地实现远程删除包, 前提是这个包在本地目录要有,比如

   
  1. osc delete test
  2.     osc up
复制代码

   
另外远程删除包和远程删除包中的文件,指令和我们对英文字面意思的感受正好是相反的。

以上所有,都可以用相应格式的

   
  1. osc undelete
复制代码

   
来恢复。

  • 我不想只等结果,怎样才能介入服务器的打包过程?


很好,osc 是可以做到的

   
  1. osc abortbuild home:MargueriteSu test openSUSE_12.1 x86_64
复制代码

   
可以取消该版本/架构的打包。但只是这一次,想永久的话用

   
  1. osc meta pkg/prj -e home:MargueriteSu test
复制代码


编辑里面的 <build></build> 字段,添加

   
  1. <disable repository="" arch=""/>
复制代码

   
字段。

   
  1. osc rebuild home:MargueriteSu test openSUSE_12.1 x86_64
复制代码

   
可以请求服务器重新编译,会开启一个文件,叫你在首行写 rebuild 的原因,不写也可以,写了可以通过

  
  1. osc triggerreason
复制代码

   
来看到。

  • 我怎么管理最终生成的包?


   
  1. osc list -b home:MargueriteSu test openSUSE_12.1 x86_64
复制代码

   
可以看任何一个看到最终的二进制包。

   
  1. osc getbinaries
复制代码


可以下载到本地。

   
  1. osc wipebinaries
复制代码

   
则是在服务器上毁掉某些二进制包。


好了,看到这里,除了一些用于「协作」的 osc 指令和跟链入其他人车库的包,或者去开其他人的包的分支相关的 osc 指令,基本上所有的「学姐常用」的 osc 命令都在这里了。

链入和开分支相关的作业会在下一节 spec 文本的「编译依赖 的解决」章节谈,「协作」的作业将会在「提交到工厂版」章节谈。

至于你们后学呢,不要太急于求成把它们全部记住,因为有好多你可能不到一定境界都不会用到,比如「远端控制」系列下的命令。用几次就会慢慢熟悉起来的。

  • 为什么没有网页版?


很简单,因为网页版做不到。





网页版除了能添加点文件,等着,然后看 log,再回到添加文件的界面点 spec 文本去简单编辑,别什么都做不了。属于适合小程式和家用级别的,不是军用级别的。

网页版最大的不足,学姐终于可以放心的告诉你们了:

不是功能少,也不是上传源文件麻烦,而是你做哪怕一个字节的文件作业,它也会自动的「召唤」一次重新编译,并且给你的程式发行版本号加上一。如果你看到有人的私人车库里程式的发行版本号普遍偏大,那可能是他们在用网页版的关系。

而且,它不支持 Changelog,没有写的地方,除非你手动添加一个空 .change 文件并每次都手动编辑。自动加给你的一堆发行版本号,表面上看是有 谁谁谁「召唤」了 重新编译,但实际上是不写入 spec 文本的 Changelog 的,所以网页版这么做,完全没有意义。

唯一有意义的自动加发行版本号,后面会讲到,是用在「service」里,用来 trigger rebuild。

好啦,本次的玛噶学姐带你学 OBS 就上课到这里,下,课!
(。・ω・)ノ゙

Marguerite
发表于 2011-12-29 21:30:23 | 显示全部楼层
目前就俺个人的需求,没有用终端版的动力啊……
回复 支持 反对

使用道具 举报

发表于 2011-12-29 22:05:47 | 显示全部楼层
搬个小板凳,前来学习,学姐继续发力哈
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-12-29 22:16:46 | 显示全部楼层
Post by 绿色圣光;2157857
目前就俺个人的需求,没有用终端版的动力啊……


就个人需求,连 OBS 动力都没有好么。

这个是为最终像 opensuse_zh 提交包的前导教学。
回复 支持 反对

使用道具 举报

发表于 2011-12-30 00:06:58 | 显示全部楼层
不错,很久没玩这个了
周末试试,期待后面的章节
回复 支持 反对

使用道具 举报

发表于 2011-12-30 00:09:37 | 显示全部楼层
另外说一下,你的那个.su的域名访问不了
可能需要翻墙吧
回复 支持 反对

使用道具 举报

发表于 2011-12-30 00:11:53 | 显示全部楼层
建议整理一份到google doc分享一下吧
也许该合并一下
回复 支持 反对

使用道具 举报

发表于 2011-12-30 19:51:37 | 显示全部楼层
Post by doublechou;2157860
就个人需求,连 OBS 动力都没有好么。

这个是为最终像 opensuse_zh 提交包的前导教学。


可是俺有动力啊……
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-12-31 00:46:21 | 显示全部楼层
你有动力就把动力开大一些,做个像学姐一样的「神秘打包侠」吧,期待在 homepensuse_zh 中看到你提交的包哦
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-12-31 00:48:43 | 显示全部楼层
Post by andycrusoe;2157874
建议整理一份到google doc分享一下吧
也许该合并一下


不会出 Google Doc 的。

因为网路上已经在 我的技术博客、sir 和 toy 发布了,不需要别的文档了。

但是目前已经在分章节的出pdf了,主要是用于向「openSUSE 文档计划」存档。最后如果能写完这个系列,我会像「鸟哥的 linux 私房菜」那样子出个电子书的。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表