<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>小李贼 &#187; python</title>
	<atom:link href="http://dipplum.com/tag/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://dipplum.com</link>
	<description>Be  the change you wanna see in the world</description>
	<lastBuildDate>Wed, 08 Sep 2010 16:45:17 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>最近用scons的收获</title>
		<link>http://dipplum.com/2009/11/13/scons-tips/</link>
		<comments>http://dipplum.com/2009/11/13/scons-tips/#comments</comments>
		<pubDate>Fri, 13 Nov 2009 07:59:28 +0000</pubDate>
		<dc:creator>li</dc:creator>
				<category><![CDATA[开源软件]]></category>
		<category><![CDATA[make]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[scons]]></category>

		<guid isPermaLink="false">http://dipplum.com/2009/11/13/scons-tips/</guid>
		<description><![CDATA[背景 Windows平台上的编译工程工具很多，Visual C++ 6的dsp文件，2003以后用的vcproj，以及最近2010支持的用msbuild编译的vcxproj，加上古老的nmake，cygwin、msys移植的make，等等很多。 make的问题是扩展性比较差，尤其是在Windows平台上，nmake的功能更弱，导致写一个工程文件很费劲，管理多个工程有大量的重复工作要做。make最大的问题是不能automake那种简洁的工程写法（automake同样存在扩展问题，m4语言不懂）. 最新的msbuild 4.0试用了一下，感觉不是很好，很费劲。因为最近迷恋上了命令行，一直不愿意安装完整的Visual Studio 2010 Beta 2，直接把2010的命令行拷贝出来用cl.exe之类的。想只装.Net Framework 4.0就试试msbuild，发现不行，把2010里的msbuild相关资源拷贝出来也不行。而且用XML手写工程文件很麻烦。 猛然想起之前听说过，鼎鼎大名，但之前简单了解后放弃了的scons。再次使用之后，才发现scons的妙处。 简介Scons Scons首页赫然是Eric Raymond的推荐，很是唬人的样子。Scons是python写的，这不重要。Scons脚本用语言是python，这是Scons最吸引人的地方。用python写工程文件无疑对我这种熟悉python的人有很大的吸引力。 实际上，Scons最大的优势，就是描述语言本身沿用python。这种设计极大的提升的Scons的扩展能力。Scons本身功能是否强大不重要，对于熟悉python的人来说，扩展Scons达到他的要求，变得比以前简单的多。下面是我用Scons写的一个zlib的工程文件，其中WinLib和WinDLL是我自己扩展的在Windows平台上编译静态库和动态库的方法（cl的宏定义和link的参数有所不同）。 target = 'libz' defs = 'ASMV ASMINF' incs = '.' &#160; Import&#40;'env'&#41; lib_src = Split&#40;'contrib/masmx86/inffas32.asm contrib/masmx86/gvmat32.asm adler32.c compress.c crc32.c deflate.c gzio.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c contrib/masmx86/gvmat32c.c'&#41; lib_src += env.RES&#40;'win32/zlib1.rc'&#41; dll_src = lib_src + Split&#40;'win32/zlib.def'&#41; [...]]]></description>
			<content:encoded><![CDATA[<h4>背景</h4>
<p>Windows平台上的编译工程工具很多，Visual C++ 6的dsp文件，2003以后用的vcproj，以及最近2010支持的用msbuild编译的vcxproj，加上古老的nmake，cygwin、msys移植的make，等等很多。</p>
<p>make的问题是扩展性比较差，尤其是在Windows平台上，nmake的功能更弱，导致写一个工程文件很费劲，管理多个工程有大量的重复工作要做。make最大的问题是不能automake那种简洁的工程写法（automake同样存在扩展问题，m4语言不懂）.</p>
<p>最新的msbuild 4.0试用了一下，感觉不是很好，很费劲。因为最近迷恋上了命令行，一直不愿意安装完整的Visual Studio 2010 Beta 2，直接把2010的命令行拷贝出来用cl.exe之类的。想只装.Net Framework 4.0就试试msbuild，发现不行，把2010里的msbuild相关资源拷贝出来也不行。而且用XML手写工程文件很麻烦。</p>
<p>猛然想起之前听说过，鼎鼎大名，但之前简单了解后放弃了的scons。再次使用之后，才发现scons的妙处。</p>
<h4></h4>
<p> <span id="more-432"></span><br />
<h4>简介Scons</h4>
<p>Scons首页赫然是Eric Raymond的推荐，很是唬人的样子。Scons是python写的，这不重要。Scons脚本用语言是python，这是Scons最吸引人的地方。用python写工程文件无疑对我这种熟悉python的人有很大的吸引力。</p>
<p>实际上，Scons最大的优势，就是描述语言本身沿用python。这种设计极大的提升的Scons的扩展能力。Scons本身功能是否强大不重要，对于熟悉python的人来说，扩展Scons达到他的要求，变得比以前简单的多。下面是我用Scons写的一个zlib的工程文件，其中WinLib和WinDLL是我自己扩展的在Windows平台上编译静态库和动态库的方法（cl的宏定义和link的参数有所不同）。</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">target = <span style="color: #483d8b;">'libz'</span>
defs = <span style="color: #483d8b;">'ASMV ASMINF'</span>
incs = <span style="color: #483d8b;">'.'</span>
&nbsp;
Import<span style="color: black;">&#40;</span><span style="color: #483d8b;">'env'</span><span style="color: black;">&#41;</span>
lib_src = Split<span style="color: black;">&#40;</span><span style="color: #483d8b;">'contrib/masmx86/inffas32.asm contrib/masmx86/gvmat32.asm adler32.c compress.c crc32.c deflate.c gzio.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c contrib/masmx86/gvmat32c.c'</span><span style="color: black;">&#41;</span>
lib_src += env.<span style="color: black;">RES</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'win32/zlib1.rc'</span><span style="color: black;">&#41;</span>
dll_src = lib_src + Split<span style="color: black;">&#40;</span><span style="color: #483d8b;">'win32/zlib.def'</span><span style="color: black;">&#41;</span>
&nbsp;
Import<span style="color: black;">&#40;</span><span style="color: #483d8b;">'WinLib WinDLL'</span><span style="color: black;">&#41;</span>
lib = WinLib<span style="color: black;">&#40;</span>target, lib_src, CPPDEFINES=defs, CPPPATH=incs<span style="color: black;">&#41;</span>
dll = WinDLL<span style="color: black;">&#40;</span>target, dll_src, CPPDEFINES=defs, CPPPATH=incs<span style="color: black;">&#41;</span>
&nbsp;
Alias<span style="color: black;">&#40;</span>target, <span style="color: black;">&#91;</span>lib, dll<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>实际上Scons本身的功能比较有限，为了写成这个简洁的模式，还费了不少劲。</p>
<h4>Scons的特点</h4>
<p>简单列举下最近有体会的Scons的特点，权当备忘录了。</p>
<p>1. Variables的设计很好。</p>
<p>通过Variables可以扩展scons支持的命令行参数，限制参数种类（Bool，枚举，多选），可以把上次的参数保存到文件，下次编译不需要命令行输入过多的参数。下面摘下我的Variable定义：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #008000;">vars</span> = Variables<span style="color: black;">&#40;</span><span style="color: #483d8b;">'options.ini'</span><span style="color: black;">&#41;</span>
<span style="color: #008000;">vars</span>.<span style="color: black;">AddVariables</span><span style="color: black;">&#40;</span>
    BoolVariable<span style="color: black;">&#40;</span><span style="color: #483d8b;">'verbose'</span>, <span style="color: #483d8b;">'Show verbose messages'</span>, <span style="color: #483d8b;">'no'</span><span style="color: black;">&#41;</span>,
    BoolVariable<span style="color: black;">&#40;</span><span style="color: #483d8b;">'unicode'</span>, <span style="color: #483d8b;">'Use unicode API'</span>, <span style="color: #483d8b;">'yes'</span><span style="color: black;">&#41;</span>,
    BoolVariable<span style="color: black;">&#40;</span><span style="color: #483d8b;">'debug'</span>, <span style="color: #483d8b;">'Generate debug code'</span>, <span style="color: #483d8b;">'no'</span><span style="color: black;">&#41;</span>,
    EnumVariable<span style="color: black;">&#40;</span><span style="color: #483d8b;">'warn'</span>, <span style="color: #483d8b;">'Show warning level'</span>, <span style="color: #483d8b;">'no'</span>, <span style="color: black;">&#91;</span><span style="color: #483d8b;">'no'</span>, <span style="color: #483d8b;">'low'</span>, <span style="color: #483d8b;">'all'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>,
    EnumVariable<span style="color: black;">&#40;</span><span style="color: #483d8b;">'optimize'</span>, <span style="color: #483d8b;">'Set optimization mode'</span>, <span style="color: #483d8b;">'normal'</span>, <span style="color: black;">&#91;</span><span style="color: #483d8b;">'normal'</span>, <span style="color: #483d8b;">'max'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>,
    PathVariable<span style="color: black;">&#40;</span><span style="color: #483d8b;">'repos_root'</span>, <span style="color: #483d8b;">'Path to root of source repository'</span>, <span style="color: #483d8b;">'..'</span><span style="color: black;">&#41;</span>,
    PathVariable<span style="color: black;">&#40;</span><span style="color: #483d8b;">'build_root'</span>, <span style="color: #483d8b;">'Path to root of build directory'</span>, <span style="color: #483d8b;">'build'</span>,
        PathVariable.<span style="color: black;">PathIsDirCreate</span><span style="color: black;">&#41;</span>,
    PathVariable<span style="color: black;">&#40;</span><span style="color: #483d8b;">'output_root'</span>, <span style="color: #483d8b;">'Path to output directory'</span>, <span style="color: #483d8b;">'.'</span>,
        PathVariable.<span style="color: black;">PathIsDirCreate</span><span style="color: black;">&#41;</span>,
<span style="color: black;">&#41;</span>
env = Environment<span style="color: black;">&#40;</span>ENV = <span style="color: #dc143c;">os</span>.<span style="color: black;">environ</span>, variables = <span style="color: #008000;">vars</span><span style="color: black;">&#41;</span></pre></div></div>

<p>2. Environment的设计也很好。</p>
<p>Environment可以保存全套的编译命令，参数配置，用Clone操作可以从一个基本编译环境作出很多变种，支持不同项目类型，不同配置的编译。Clone实在是太方便了。下面是前面提到的Windows平台下编译DLL和静态库、命令行程序的不同参数配置，用Clone操作可以在基本编译环境上，生成多个用于编译静态库、DLL等不同的编译环境，使用不同的参数编译。</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">libEnv = env.<span style="color: black;">Clone</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
libEnv.<span style="color: black;">Append</span><span style="color: black;">&#40;</span>CPPDEFINES = Split<span style="color: black;">&#40;</span><span style="color: #483d8b;">'_WINDOWS _WINDLL'</span><span style="color: black;">&#41;</span>, LINKFLAGS = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'/SUBSYSTEM:WINDOWS'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
dllEnv = env.<span style="color: black;">Clone</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
dllEnv.<span style="color: black;">Append</span><span style="color: black;">&#40;</span>CPPDEFINES = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'_WINDOWS'</span><span style="color: black;">&#93;</span>, LINKFLAGS = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'/SUBSYSTEM:WINDOWS'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
conEnv = env.<span style="color: black;">Clone</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
conEnv.<span style="color: black;">Append</span><span style="color: black;">&#40;</span>CPPDEFINES = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'_CONSOLE'</span><span style="color: black;">&#93;</span>, LINKFLAGS = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'/SUBSYSTEM:CONSOLE'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>题外话，关于Windows下编译不同工程的参数，<a href="http://binglongx.spaces.live.com/blog/cns!142CBF6D49079DE8!276.entry" target="_blank">这篇文章</a>总结的很全。</p>
<p>3. Builder的设计问题。</p>
<p>Scons可以用env.Library(&#8216;liba&#8217;, &#8216;a.c&#8217;)的方法用环境的定义编译静态库，或者用Program定义可执行程序。并且支持输入和环境定义不同的参数，如</p>
<p>env.Library(&#8216;liba&#8217;, &#8216;a.c&#8217;, CPPDEFINES = ['NEWMACRO'], CCFLAGS=['/Ox'])。</p>
<p>不过，很可惜，Builder默认是覆盖环境中现有参数，而不是追加。所以，要用多个编译参数稍有不同的程序和库编译只能用Clone后Append。</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">tempEnv = libEnv.<span style="color: black;">Clone</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
tempEnv.<span style="color: black;">Append</span><span style="color: black;">&#40;</span>CPPDEFINES = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'NEWMACRO'</span><span style="color: black;">&#93;</span>, CCFLAGS=<span style="color: black;">&#91;</span><span style="color: #483d8b;">'/Ox'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
tempEnv.<span style="color: black;">Library</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'liba'</span>, <span style="color: #483d8b;">'a.c'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>或许我要求太高，不过这样写看起来实在太傻了。实际使用中，不同的程序用的编译参数就没有完全一样的，尤其是Include目录、宏定义、库路径、库这四个参数肯定有或多或少的不一样。不能追加的话，就只能Clone出很多个Env，甚至是一个Program或Library一个Env，这就失去了Env封装编译环境的通用意义了。</p>
<p>4. Sconscript与源码的位置问题</p>
<p>Scons设计时更多是支持将Sconscript放在源码同一目录下，甚至每个源码目录一个Soncscript。如果不习惯或者不能用这种方式，比如为别的项目写工程文件，遇到一些困扰。</p>
<p>Scons好在有类似make的VPATH功能，叫Repository，用着还行。</p>
<p>5. 源码和编译目录分开的问题</p>
<p>这个问题和上面类似，但更严重。Scons支持用VariantDir设定与源码目录不同的编译目录，但必须所有的文件都用编译目录的路径，如下</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">VariantDir<span style="color: black;">&#40;</span><span style="color: #483d8b;">'build_dir'</span>, <span style="color: #483d8b;">'src_dir'</span><span style="color: black;">&#41;</span>
Program<span style="color: black;">&#40;</span><span style="color: #483d8b;">'build_dir/prog'</span>, <span style="color: #483d8b;">'build_src/main.c'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>这就很傻了，明明源码在src_dir目录，一定要写成build_src。不这么写也行，那就写两个Sconcript文件，一个引用另外一个</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#SConscriptA</span>
Program<span style="color: black;">&#40;</span><span style="color: #483d8b;">'prog'</span>, <span style="color: #483d8b;">'src/main.c'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;">#SConscriptB</span>
SConscript<span style="color: black;">&#40;</span><span style="color: #483d8b;">'SconscriptA'</span>, variant_dir=<span style="color: #483d8b;">'build'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>要用好VariantDir，就必须写多个脚本。尤其是要支持编译同一程序的多个版本，还只能用两个脚本。SconscriptB可以按如下方式写，就能编译两个版本的prog，分别在debug和release目录下。用一个Sconscript完成同样功能实在是很费劲的。</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">SConscript<span style="color: black;">&#40;</span><span style="color: #483d8b;">'SconscriptA'</span>, variant_dir=<span style="color: #483d8b;">'debug'</span><span style="color: black;">&#41;</span>
SConscript<span style="color: black;">&#40;</span><span style="color: #483d8b;">'SconscriptA'</span>, variant_dir=<span style="color: #483d8b;">'release'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>6. 同一个源码编译多份的问题</p>
<p>这个问题和上面类似，但不同。例如编译静态库和动态库都用一批源码，但编译参数不一样，Scons就会报错。但这个问题用variant_dir解决不方便。如下例：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">Program<span style="color: black;">&#40;</span><span style="color: #483d8b;">'hello1'</span>, <span style="color: #483d8b;">'hello.c'</span>, CCFLAGS=<span style="color: black;">&#91;</span><span style="color: #483d8b;">'-Od'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
Program<span style="color: black;">&#40;</span><span style="color: #483d8b;">'hello2'</span>, <span style="color: #483d8b;">'hello.c'</span>, CCFLAGS=<span style="color: black;">&#91;</span><span style="color: #483d8b;">'-Ox'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Scons报错如下：</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">scons: <span style="color: #000000; font-weight: bold;">***</span> Two environments with different actions were specified <span style="color: #000000; font-weight: bold;">for</span> the same target:</pre></div></div>

<p>经过我多方论证，发现这个问题目前看来最佳、最简单的解决方法是给OBJ文件设置前缀。</p>
<pre>Program('hello1', 'hello.c', OBJPREFIX='dbg-', CCFLAGS=['-Od'])
Program('hello2', 'hello.c', OBJPREFIX='opt-', CCFLAGS=['-Ox'])</pre>
<p>7. 简化编译输出的问题</p>
<p>Scons支持自定义的编译输出。但有些命令不能替换，例如msvc工具集中的链接过程的输出。</p>
<p>暂时这么多吧&#8230;想到再补充。</p>
]]></content:encoded>
			<wfw:commentRss>http://dipplum.com/2009/11/13/scons-tips/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
