使用 Docker 构建 PetaLinux 开发环境
2017-07-26 20:00
435 查看
最后更新:2017-08-11
源码地址:https://github.com/xaljer/petalinux-docker
镜像地址:https://hub.docker.com/r/xaljer/petalinux
本文地址:http://blog.csdn.net/elegant__/article/details/76162435
安装开发环境的依赖稍显复杂,且根据操作系统不同而异,新手不易搞定。安装过程没有被很好的文档化,团队成员重复劳动明显。
统一操作系统版本困难。开发人员(现有成员、未来加入成员、项目其他团队的成员)可能使用着不同的 Linux 发行版和版本,因为他们可能有不同的喜好,版本上喜欢尝鲜或守旧,在既有系统上已经有顺手的开发工具和设置等等。而当大家希望统一开发环境或自行搭建失败时,往往选择复制整个虚拟机镜像。
使用复制整个虚拟机的方式传递开发环境不够灵活。表现为:
使用了一段时间后的虚拟机大小可能动辄四五十个 GB,拷贝时间长,难以通过网络共享。
为保留可回退的环境,可能为虚拟机添加快照,但这会进一步显著增加虚拟机的体积。
传递虚拟机不仅传递了需要的开发环境,还传递了大量无用的软件和个人设置,难以兼顾不同开发者之间的习惯差异。
对于不同项目、用途可能存在多个虚拟机,资源冗余很大。
使用虚拟机性能表现差。主机配置不高时,使用虚拟机(可能会再虚拟机内运行 IDE 等其他开发工具)会经常卡顿,降低了开发效率。
使用为某一开发环境而复制来的虚拟机,迁就其环境不愿再安装合适的开发工具(如 IDE 等)。
经过压缩的镜像体积只有 1GB 左右,方便网络传输。
很容易再团队中统一开发环境,包括操作系统和各种库。Docker 镜像的只读特性保证了有一个可回退的一致环境。
开发环境中的依赖被 Dockerfile 清晰、明确的记录下来,具有很好的文档效应,方便团队长期共享和维护。
Docker 可以运行在之前的虚拟机的操作系统上,也可以运行在一个“精简”的操作系统上(如 Docker for Windows 的方式),还可以放到服务器上,运行多个容器供团队成员使用。这使得使用 Linux 环境的方式更加多样、灵活,且能减少虚拟机性能原因带来的影响。
现在理想很丰满,但现实中还需要经过一番探究和试验,下面就让我们开始。
其中
这里有两点需要注意。一是
我们其实也可以把 PetaLinux 提供的
这里我们使用
由于在 Ubuntu 16.04 上安装 GCC 会默认安装 gcc 5 的版本,而 2014.4 版本的 PetaLinux 应该没有适配 gcc 5,会出现很多警告。这里采取的办法是安装
在网上搜索时发现,一般资料都没有介绍如何直接更改一个软连接的指向。不知道的情况下,就只能先删除再重建这个链接了。最终还是在 Stack Overflow 上找到了答案,其实我们可以使用
第一行声明使用 expect 这个工具来解释此脚本,
第二行设置等待超时,因为 PetaLinux 的安装过程比较慢,这里将其设为
第三行设置一个变量来接收此脚本的参数,我们借此来指定希望将 PetaLinux 安装到哪个目录下。注意 expect 脚本设置参数的方式和 bash 脚本不同,参数 0 代表我们调用脚本时给出的第一个参数,而在 bash 脚本中,参数 0 代表脚本本身的名字。
第五行用
接下来我们用
PetaLinux 的安装包比较大(2014.4 版有 1.2GB,而 2016.4 已经到了丧心病狂的 8.3 GB),在安装完成后,安装包再留在镜像中已经没有什么意义了,还会显著的增加镜像的体积。这里要理解 Docker 的镜像是由一个个的层(layer)组成的,Dockerfile 中的每一条指令都对应于一层,每一层都是在前一层的基础上进行的增量的改变。这意味着,一旦我们在某一层中引入了一个文件,即使在下一层中将其删除,对体积的减小也无济于事,我们只是无法在最终的容器中“看见”它们而已。如果我们使用
这里使用网络是更好的方式,一方面如果我们在互联网或者私有服务器上存放了安装包,通过更改
在外部,我使用了一个脚本来封装启动 HTTP 服务器、构建 Docker 镜像、停止服务器的步骤:
这个脚本的第一个参数是安装包在本地的路径。首先让服务器在后台建立,并记录下其对应的 pid,在完成镜像的构建后再将其杀死。Python 创建的服务器会默认监听 8000 端口。在容器内部(Docker 构建的过程即相当于在临时的容器中执行 Dockerfile 的过程)可以通过 Docker 的默认网桥(
使用
运行容器:
在容器中创建工程并编译:
PetaLinux 会提示找不到 tftp,这是因为没有对其进行进一步的配置。如果不使用 tftp 可以忽略这个问题。
在 Docker for Windows 下构建时,可能会出现错误7,将存储驱动更改为
PetaLinux 工具的名字都有点长,可以考虑在镜像里对常用的操作添加别名。但在容器外部作可能会更方便一些,因为我们不必交互式的进入容器,而是使用
有了标准化环境,不仅可以在自己的电脑上运行,还希望放在私有服务器上,让大家通过网络访问。要达到这样的目的,一要能通过 SSH 访问容器,二要能在服务器的数据卷和本地计算机之间同步数据(源码及编译结果)。对于 SSH,初步设想可以通过外部的一些 Docker 工具来完成,而不是在容器内部建立 SSH 服务器,因为有多个容器时,要对应多个不同端口等问题。对于数据同步,可以在本地的 Windows 系统上通过 Linux 子系统(WSL)建立 NFS 服务器,在容器内部挂载 NFS,或者通过 Docker 的插件实现直接将远端的 NFS 作为数据卷挂载。
使用虚拟机在 Windows 下搭建嵌入式开发环境是以往非常常用的方式,但也是一种比较笨重的方式。随着一些新的技术、平台的出现,如 Docker 和 WSL,我们可以尝试利用它们搭建开发环境,提升开发的效率。
Best practices for writing Dockerfiles ↩
PetaLinux Reference Guide ↩
unable to locate packag lib32bz2-1-0 ↩
how to run 32 bit app in ubuntu 64 bit ↩
how to add a file to an image in dockerfile without using the add or copy direct ↩
fails on ‘tar’ with: “Directory renamed before its status could be extracted” ↩
源码地址:https://github.com/xaljer/petalinux-docker
镜像地址:https://hub.docker.com/r/xaljer/petalinux
本文地址:http://blog.csdn.net/elegant__/article/details/76162435
0. 背景
0.1 PetaLinux 是什么
PetaLinux 是 Xilinx 推出的用于在其自家 SoC 上构建嵌入式 Linux 的一套工具集,集成了编译、调试、仿真等众多工具。0.2 原有的搭建 PetaLinux 开发环境的方式有什么问题
实验室的项目基于 Xilinx 的 Zynq 系列 SoC 开发,需要使用 PetaLinux 构建嵌入式 Linux 系统,第一步就是搭建开发环境。团队成员平时的主力系统是 Windows,使用虚拟机的方式搭建 Linux 开发环境。搭建开发环境出现困难,或为节省时间的目的时会从其他成员那里复制搭建好环境的虚拟机。这些方式已产生或可能产生的问题可以总结如下:安装开发环境的依赖稍显复杂,且根据操作系统不同而异,新手不易搞定。安装过程没有被很好的文档化,团队成员重复劳动明显。
统一操作系统版本困难。开发人员(现有成员、未来加入成员、项目其他团队的成员)可能使用着不同的 Linux 发行版和版本,因为他们可能有不同的喜好,版本上喜欢尝鲜或守旧,在既有系统上已经有顺手的开发工具和设置等等。而当大家希望统一开发环境或自行搭建失败时,往往选择复制整个虚拟机镜像。
使用复制整个虚拟机的方式传递开发环境不够灵活。表现为:
使用了一段时间后的虚拟机大小可能动辄四五十个 GB,拷贝时间长,难以通过网络共享。
为保留可回退的环境,可能为虚拟机添加快照,但这会进一步显著增加虚拟机的体积。
传递虚拟机不仅传递了需要的开发环境,还传递了大量无用的软件和个人设置,难以兼顾不同开发者之间的习惯差异。
对于不同项目、用途可能存在多个虚拟机,资源冗余很大。
使用虚拟机性能表现差。主机配置不高时,使用虚拟机(可能会再虚拟机内运行 IDE 等其他开发工具)会经常卡顿,降低了开发效率。
使用为某一开发环境而复制来的虚拟机,迁就其环境不愿再安装合适的开发工具(如 IDE 等)。
0.3 基于 Docker 的解决方案
Docker 作为在很多场景下虚拟机的替代方案备受瞩目,其资源消耗小、为单一应用配置环境、易于通过网络共享等特点很好的解决了上面提到的诸多问题。通过 Docker 来构建 PetaLinux 开发环境,我们可以获得以下优势:经过压缩的镜像体积只有 1GB 左右,方便网络传输。
很容易再团队中统一开发环境,包括操作系统和各种库。Docker 镜像的只读特性保证了有一个可回退的一致环境。
开发环境中的依赖被 Dockerfile 清晰、明确的记录下来,具有很好的文档效应,方便团队长期共享和维护。
Docker 可以运行在之前的虚拟机的操作系统上,也可以运行在一个“精简”的操作系统上(如 Docker for Windows 的方式),还可以放到服务器上,运行多个容器供团队成员使用。这使得使用 Linux 环境的方式更加多样、灵活,且能减少虚拟机性能原因带来的影响。
现在理想很丰满,但现实中还需要经过一番探究和试验,下面就让我们开始。
1. 构建 Docker 镜像
Docker 的优势就在于我们可以使用“代码”来表示需要的环境,它既能描述环境,也能直接指导生成环境,这份“代码”就是 Dockerfile。下面详细的记述了这份 Dockerfile 的每一部分,过程中遇到的问题、解决办法、注意事项等。关于 Dockerfile,可以参考官方的 Dockerfile reference1,以及 Best practices for writing Dockerfiles2。1.1 设置构建参数
Dockerfile 中允许使用ARG指令设置构建时参数,这些参数在 Dockerfile 中具有默认值,在构建时可以通过
--build-arg参数指定新的值来覆盖默认值。这些参数可以在 Dockerfile 中被引用(引用方式与在 shell 中引用变量一样),但不会出现在最终的镜像里。注意一条
ARG指令只能指定一个参数,这一点和
ENV指令是不同的。这里我设置了两个参数如下:
ARG install_dir=/opt ARG installer_url=172.17.0.1:8000
其中
install_dir用来指定 PetaLinux 的安装路径,
installer_url用来指定 PetaLinux 安装包的网络地址。如果安装包在互联网上,则这里是一个访问链接,如果安装包在本地,则这里被指定为 Docker 的默认网桥,通过它联通本地网络服务器和构建时的临时容器。关于这一部分,我会在后面详述。
1.2 设置环境变量
通常情况下,PetaLinux 使用一个设置脚本来添加自身的各项工具到环境变量中,在使用相关工具前需要通过source <install_directory>/settings.sh来执行脚本。但现在我要制作一个专属于 PetaLinux 的环境,完全可以把环境变量设置好来免去这个步骤。在 Dockerfile 中使用
ENV指令来设置环境变量:
ENV PETALINUX_VER=2014.4 \ PETALINUX=${install_dir}/petalinux-v2014.4-final ENV PATH="${PETALINUX}/tools/linux-i386/arm-xilinx-gnueabi/bin:\ ${PETALINUX}/tools/linux-i386/arm-xilinx-linux-gnueabi/bin:\ ${PETALINUX}/tools/linux-i386/microblaze-xilinx-elf/bin:\ ${PETALINUX}/tools/linux-i386/microblazeel-xilinx-linux-gnu/bin:\ ${PETALINUX}/tools/linux-i386/petalinux/bin:\ ${PETALINUX}/tools/common/petalinux/bin:\ ${PATH}"
这里有两点需要注意。一是
ENV虽然支持并推荐在一条指令下设置多个环境变量,但如果这些环境变量之间存在相互引用的情况,就在分开写了。比如这里设置
PATH变量时引用了
PETALINUX变量,它们就不能在同一个
ENV指令下进行设置了。二是
PATH这个变量中每个路径之间不可以有空格,否则是搜索不到可执行文件的,所以这里也只能不顾缩进来保证没有空格了。网络上似乎没有什么解决这个问题的讨论,而且在
ENV指令下我们无法使用任何其他的工具去处理这个字符串。
我们其实也可以把 PetaLinux 提供的
settings.sh脚本添加到
.bashrc文件中,使得其每次被自动执行。实际上这个脚本中除了配置环境变量,最后还运行了 PetaLinux 自带的一个环境检查工具,用于检查网络、磁盘剩余空间等信息,使用前述设置环境变量的方式就忽略这个检查工具了。
1.3 安装依赖
在 PetaLinux 的参考指南3中给出了它所依赖的工具和库,然而并不全面和准确。一是因为有些包已经被替代,现在无法获得4;二是对于 32 位库支持5只是一笔带过,并未具体列出;三是有的包可能因非常基础而未列出,但是在 Docker 的基础镜像中却没有包含,如bc。以下是我测试成功的、在当前基础镜像下需要的所有依赖:
RUN dpkg --add-architecture i386 && \ apt-get update && apt-get install -y --no-install-recommends \ # Required tools and libraries of Petalinux. # See in: ug1144-petalinux-tools-reference-guide, v2014.4. tofrodos \ iproute \ gawk \ gcc-4.7 \ git-core \ make \ net-tools \ rsync \ wget \ tftpd-hpa \ zlib1g-dev \ flex \ bison \ bc \ lib32z1 \ lib32gcc1 \ libncurses5-dev \ libncursesw5-dev \ libncursesw5:i386 \ libncurses5:i386 \ libbz2-1.0:i386 \ libc6:i386 \ libstdc++6:i386 \ libselinux1 \ libselinux1:i386 \ # Using expect to install Petalinux automatically. expect \ && rm -rf /var/lib/apt/lists/* /tmp/* \ && ln -fs gcc-4.7 /usr/bin/gcc \ && ln -fs gcc-ar-4.7 /usr/bin/gcc-ar \ && ln -fs gcc-nm-4.7 /usr/bin/gcc-nm \ && ln -fs gcc-ranlib-4.7 /usr/bin/gcc-ranlib
这里我们使用
--no-install-recommends参数来避免安装不必要的包,并在安装结束后清理
/var/lib/apt/lists/和
/tmp/目录,以尽可能的使镜像精简。
由于在 Ubuntu 16.04 上安装 GCC 会默认安装 gcc 5 的版本,而 2014.4 版本的 PetaLinux 应该没有适配 gcc 5,会出现很多警告。这里采取的办法是安装
gcc-4.7,并修改符号链接,使
/usr/bin/gcc指向这一版本的 GCC。事实上我并不确切知道应该安装哪个版本,在 PetaLinux 2016.4 中指定了使用 gcc 4.8。这里影响应该不大,因为真正用于构建项目的交叉编译器是 PetaLinux 自带的。
在网上搜索时发现,一般资料都没有介绍如何直接更改一个软连接的指向。不知道的情况下,就只能先删除再重建这个链接了。最终还是在 Stack Overflow 上找到了答案,其实我们可以使用
-f选项在一条命令中更改软连接的指向。这个技巧在后面还会用到。
1.4 使用 expect 脚本自动安装 PetaLinux
PetaLinux 的安装包在安装过程中会显示许可证协议,并要求用户输入确认信息。这样的交互方式给我们的自动化处理造成了一点小困难,然而程序员前辈们肯定是不允许这种不能自动化的情况持续的,expect 这个工具就是专门用来自动处理这种需要交互输入的情况的。expect 常常用来处理 SSH 登陆等需要交互输入密钥的情况,它会监视一个程序的输出,并在捕获到了特定的输出后给出一个预设的输入。这里我们使用一个auto-install.sh脚本来自动安装 PetaLinux,脚本的内容如下:
#!/usr/bin/env expect set timeout -1 set install_dir [lindex $argv 0] spawn ./petalinux-v2014.4-final-installer.run $install_dir expect "Press Enter to display the license agreements" send "\r" expect "*>*" send "y\r" expect "*>*" send "y\r" expect eof
第一行声明使用 expect 这个工具来解释此脚本,
/usr/bin/env会遍历
PATH变量来寻找后面的可执行文件,这样避免了依赖于 expect 的安装路径。
第二行设置等待超时,因为 PetaLinux 的安装过程比较慢,这里将其设为
-1,即一直等待。
第三行设置一个变量来接收此脚本的参数,我们借此来指定希望将 PetaLinux 安装到哪个目录下。注意 expect 脚本设置参数的方式和 bash 脚本不同,参数 0 代表我们调用脚本时给出的第一个参数,而在 bash 脚本中,参数 0 代表脚本本身的名字。
第五行用
spawn命令去执行安装程序。PetaLinux 的安装程序的第一个参数也是安装路径。
接下来我们用
expext命令来捕获程序的输出,用
send命令发送预设的输入。安装程序提示你确认协议的语句是这样的:
Do you accept this license? [y/N] >,直接用
expext匹配这一句会有问题,因为至少
[]在 expect 的语法中是有特定含义的,需要转义。已无心情研究 expect 那奇怪的语法,所幸它有很棒的模糊匹配功能,我们只需要匹配最后一个
>字符就可以了。
1.5 减小镜像体积
PetaLinux 的安装包在 Xilinx 官方网站上可以下载,但需要先注册,没有固定的下载链接。所以要么需要在构建前把它下载到本地,要么在互联网上寻找一个合适的托管地点,可以提供稳定的下载链接。PetaLinux 的安装包比较大(2014.4 版有 1.2GB,而 2016.4 已经到了丧心病狂的 8.3 GB),在安装完成后,安装包再留在镜像中已经没有什么意义了,还会显著的增加镜像的体积。这里要理解 Docker 的镜像是由一个个的层(layer)组成的,Dockerfile 中的每一条指令都对应于一层,每一层都是在前一层的基础上进行的增量的改变。这意味着,一旦我们在某一层中引入了一个文件,即使在下一层中将其删除,对体积的减小也无济于事,我们只是无法在最终的容器中“看见”它们而已。如果我们使用
COPY指令将 PetaLinux 的安装包添加进去,则
COPY指令会生成一个层,我们无法再把它产生的体积抹除掉。Stack Overflow 上有一个关于这个问题的讨论6,主要提到了三种方式:一是在本地构建一个网络服务器,通过网络的方式传到 Docker 容器的内部,我采用了这种方式,后面详述;二是不能使用 Dockerfile 的方式构建容器,而是在容器中完成安装和清理工作后手动提交更改到镜像;三是使用第三方工具对生成的镜像进行再压缩。
这里使用网络是更好的方式,一方面如果我们在互联网或者私有服务器上存放了安装包,通过更改
installer_url变量就可以使用新的地址获取文件;另一方面,在本地可以使用 Python 轻松的创建一个 HTTP 服务器。在 Dockerfile 中,我们使用
wget下载安装包、配置其权限、运行自动安装脚本,最后删除安装包。这些步骤必须在一个
RUN指令下完成,这样安装包才不会留在最终的镜像里。
WORKDIR $install_dir COPY ./auto-install.sh . RUN wget -q $installer_url/petalinux-v2014.4-final-installer.run && \ chmod a+x petalinux-v2014.4-final-installer.run && \ ./auto-install.sh $install_dir && \ rm -rf petalinux-v2014.4-final-installer.run
在外部,我使用了一个脚本来封装启动 HTTP 服务器、构建 Docker 镜像、停止服务器的步骤:
#!/usr/bin/env bash installer_dir=$1 docker_context=`pwd` echo "Start to build petalinux tools docker image ..." echo "-----------------------------------------------" cd $installer_dir python3 -m http.server & server_pid=$! cd $docker_context installer_ip=`ifconfig docker0 | grep 'inet\s' | awk '{print $2}'` docker build -t petalinux-docker:2014.4 \ --build-arg installer_url=${installer_ip}:8000 \ . kill $server_pid echo "---------------" echo " Finish. ^_^ " echo "---------------"
这个脚本的第一个参数是安装包在本地的路径。首先让服务器在后台建立,并记录下其对应的 pid,在完成镜像的构建后再将其杀死。Python 创建的服务器会默认监听 8000 端口。在容器内部(Docker 构建的过程即相当于在临时的容器中执行 Dockerfile 的过程)可以通过 Docker 的默认网桥(
docker0)的 IP 地址来访问本地主机。网桥对应的 IP 地址并不是唯一的,Docker 是根据主机中网卡的配置不同,选择一个没有被占用的私有网段(如果 3 类私有 IP 网段都被占用了,Docker 启动时会报错),也可以自行更改,所以这里我们从
ifconfig的输出中提取
docker0对应得 IP 地址。
docker build命令中
-t参数为镜像指定标签,
--build-arg参数用来覆盖我们在 Dockerfile 内部设置的参数,最后一个参数
.指的是构建环境(build context)为当前路径。注意,如果你将安装包放在了这个构建环境的同一个目录下,一定要通过
.dockerignore文件来忽略这个安装包文件,因为否则它会被发送到 Docker daemon 上,增加构建时间且毫无用处,除非你要使用
COPY指令的方式导入安装包。
1.6 其他
PetaLinux 会检查 shell 环境,并推荐使用 Bash。在 Ubuntu:16.04 的镜像中/bin/sh这个软连接指向的是
/bin/dash,这里我们将其更改为
/bin/bash。
RUN ln -fs /bin/bash /bin/sh # bash is PetaLinux recommended shell
使用
WORKDIR指令新建了一个
/workspace的路径用于连接数据卷。最后一个
WORKDIR指定的路径就会是进入容器后的所在路径,这一点似乎官方文档没有明说。
WORKDIR /workspac
1.7 镜像的构建
如果安装包放在本地,则如 1.5 节所述,使用build-image.sh脚本构建镜像。如果安装包在互联网或本地服务器上,则直接使用
docker build命令,并使用
installer_url参数指定访问地址。
2. 测试
你可以自行按照上面的方法自行构建镜像,也可以从 Docker Hub 上下载我上传好的镜像:docker pull xaljer/petalinux:2014.4
运行容器:
docker run -ti -v /path/to/projects:/workspace xaljer/petalinux:2014.4
在容器中创建工程并编译:
petalinux-create -t project -s <path-to-bsp> -n <project-name> cd <project-name> petalinux-build # 构建整个工程,会比较慢
3. 现有问题和下一步工作
3.1 现有问题
PetaLinux 2014.4 支持的原本是 Ubuntu 14.04,但使用此版本的镜像时发现,其软件源似乎有些问题,经常安装失败,故没有使用。PetaLinux 会提示找不到 tftp,这是因为没有对其进行进一步的配置。如果不使用 tftp 可以忽略这个问题。
在 Docker for Windows 下构建时,可能会出现错误7,将存储驱动更改为
aufs后可修复。然而 Windows 下构建的镜像仍有其他问题,无法使用,作者尚未对其作更多的测试和探究。
3.2 下一步工作
添加 Vivado SDK 的一些工具。PetaLinux 工具的名字都有点长,可以考虑在镜像里对常用的操作添加别名。但在容器外部作可能会更方便一些,因为我们不必交互式的进入容器,而是使用
docker exec来执行命令,此时可以在容器外面为整个命令添加别名。
有了标准化环境,不仅可以在自己的电脑上运行,还希望放在私有服务器上,让大家通过网络访问。要达到这样的目的,一要能通过 SSH 访问容器,二要能在服务器的数据卷和本地计算机之间同步数据(源码及编译结果)。对于 SSH,初步设想可以通过外部的一些 Docker 工具来完成,而不是在容器内部建立 SSH 服务器,因为有多个容器时,要对应多个不同端口等问题。对于数据同步,可以在本地的 Windows 系统上通过 Linux 子系统(WSL)建立 NFS 服务器,在容器内部挂载 NFS,或者通过 Docker 的插件实现直接将远端的 NFS 作为数据卷挂载。
4. 总结
如果并不需要 Docker 的一些优势,我们也可以考虑将 PetaLinux 装进 Windows 的 Linux 子系统(WSL),这样可以有更好的性能和更无缝的操作。使用虚拟机在 Windows 下搭建嵌入式开发环境是以往非常常用的方式,但也是一种比较笨重的方式。随着一些新的技术、平台的出现,如 Docker 和 WSL,我们可以尝试利用它们搭建开发环境,提升开发的效率。
参考
Dockerfile reference ↩Best practices for writing Dockerfiles ↩
PetaLinux Reference Guide ↩
unable to locate packag lib32bz2-1-0 ↩
how to run 32 bit app in ubuntu 64 bit ↩
how to add a file to an image in dockerfile without using the add or copy direct ↩
fails on ‘tar’ with: “Directory renamed before its status could be extracted” ↩
相关文章推荐
- (转)EOSIO开发(一)使用Docker构建本地环境
- 关于如何使用Docker构建PHP的开发环境
- Windows7系统中使用vagrant构建Linux虚拟化开发环境
- 从零开始使用Docker构建Java Web开发运行环境
- 使用docker来构建嵌入式开发环境
- 关于如何使用Docker构建PHP的开发环境
- PHP培训 使用Docker构建PHP的开发环境
- Docker 构建 Java Web 开发环境——使用Dockerfile(二)
- 使用docker快速构建rails开发环境
- Windows 下使用 Vagrant 构建 Linux 开发环境
- 使用 Docker 构建开发、测试环境
- Windows 下使用 Vagrant 构建 Linux 开发环境,实测可用
- 从零开始使用Docker构建Java Web开发运行环境
- Linux下使用Eclipse搭建GTK+开发环境
- linux下Eclipse+CDT开发环境配置与使用
- bochs使用(linux下搭建操作系统开发环境,转)
- 使用maven构建struts2在jdk1.4.2上的开发环境
- 使用Eclipse IDE for CC++ Developers 构建C++开发环境
- VMware6.5 + ubuntu8.04 构建嵌入式linux开发环境
- 使用Eclipse IDE for CC++ Developers 构建C++开发环境