Scons环境搭建和编译原理概述及嵌入式开发常用模板

Scons是用python实现的一个类似makefile的软件构建工具。其官网是SCons: A software construction tool - SCons,其具有详细的文档来对其使用进行说明SCons Documentation。

Scons是一个软件构建工具,除了能对c/c++/asm进行构建外,也能实现对java等语言进行构建。因为其是用python来实现的,所以其具备跨平台的特性。

本例程所有相关代码在GitHub上,请自行下载。https://github.com/bobwenstudy/scons_demo

为什么用Scons

第一次接触Scons是在RTThread中,进一步了解它的特性,渐渐的就喜欢上了,平常自己写一些代码,用Scons可以方便不少。对于我个人来说,主要的好处有:

实际项目中编译工程时,除了要对代码进行编译生成固件外,还需要整合一些其他资源(如编译耳机项目时,需要讲提示音等wav资源打包进代码中;多固件合并打包之类。)之前完成这些动作是用python写的,当然能用makefile调用,但是相比Scons本身就是python,同一套环境调试起来会方便不少。

易读性,相比于makefile这么久远的构建语言,Scons是python写的,其语法和调试起来更方便,函数调用和可读性都比makefile好。

文件依赖,使用makefile时,由于经常编译的时候只将c文件作为依赖,而没有将h文件作为依赖,或其他资源,导致部分h文件更新时,经常要考虑先make clean,再make all。而Scons可以自动将关联的文件作为依赖,并构建依赖树,不用考虑太多就可以自动将相关依赖关系给维护好,当文件更新再次编译时,只编译特定文件。同时可以通过Depends指定依赖关系。

缺点也有:

集成度太强:Scons内部做了太多事情,对于我这种掌控欲比较强的程序猿来说,有点脱离掌控了,虽然方便,但是当构建大型项目时,内部一些行为可能导致debug很长时间,需要我们对其原理进行深入分析。

资料较少:毕竟是新语言,现在很多新语言很快的死在沙滩上了,对于新语言,大家或多或少保持观望态度,所以目前资料还是比较少的。遇到bug得自己看官方手册,还是有点麻烦的。

环境搭建

从上面描述可知,Scons类似于makefile,但并不是替代了GCC,只是实现了对GCC的使用。按照makefile的逻辑,需要准备如下环境:

GCC环境:笔者是Windows环境,所以需要msys64+mingw工具链。

Scons环境:python环境安装后,再安装scons就行。

GCC环境

PC下的GCC环境安装
        参考这个文章安装即可。Win7下msys64安装mingw工具链 - Milton - 博客园

安装完以后,键入gcc -v,得到如下提示说明环境安装好了,目前笔者用的是mingw32的gcc。

ARM下的GCC环境安装

从各个芯片产商都可以获取到对应的交叉工具链,笔者的交叉工具链解压缩后,就有gcc环境,一般交叉工具链会有前缀。

安装完以后,键入arm-none-eabi-gcc -v,出现如下信息就代表安装成功了。

Scons环境

Python安装

Scons是运行在python环境下的,目前新版本的都运行在Python3+的版本上,就不建议安装Python2的环境了(当然也可以用Python2就是了,不过最新的pip已经不支持Python2了,希望大家早点切换)。

网上有很多教程了,Python安装教程-史上最全_壬杰的博客-CSDN博客_python安装教程可以参考这个,当然也可以直接到Welcome to Python.org下载。

最终安装结束后,键入python -V,能看到python版本,代表环境安装好了。

Scons安装

使用python -m pip install scons即可。当然可以到官网看看SCons DownloadsSCons DownloadsSCons Downloads。

最终安装结束后,键入scons -v,能看到如下信息,代表环境安装好了。

初识Scons

基本概述

要讲清楚Scons,需要理解gcc是如何使用的,已经相比于make,scons的行为差异。首先对gcc、make和scons基本概念进行说明。

gcc
        实际编译c文件的动作都是由gcc来完成的,scons/make等构建语言只是完成了对多个编译文件和编译目标的调度管理,本身不完成编译文件的动作。

c编译原理的概念很多大佬已经讲得比较多了,可以参考:C编译原理_daixiao3636的博客-CSDN博客_c编译原理。当然现在要编译文件也很简单了,一般我们只用2步,一个是通过gcc -c -o生成object文件,然后再将object文件gcc -o生成目标文件。

make
        使用的换需要安装make环境,一般安装好mingw的时候,就自动装好了make+gcc环境,需要注意的是,新版装好后没有make.exe,而是:mingw32-make.exe,可以复制一份,并修改文件名为make.exe,以方便后面使用。

make使用可以直接在终端上使用,键入make -v,看到如下信息,说明make环境安装成功了。

执行make,会去寻找文件:GNUmakefile、makefile 和 Makefile。详细可以见:认识Makefile文件_天糊土的博客-CSDN博客_makefile文件在哪

Scons

之前已经安装好了scons环境,要使用scons,和make类似,也需要一个脚本文件,配置要执行的操作。文件名为:SConstruct

单文件构建

gcc编译

单个文件编译,gcc直接命令行操作就行,两行命令就可以搞定(当然1条命令也可以,这里为了统一,都要求先生成object文件,再生成目标文件)。

main.c的源码如下:

#include <stdio.h>
int main()
{printf("hello, world\n");return 0;
}

make编译

如图所示,第一次编译的时候,编译目标all依赖main.o,由于没有main.o,先对main.c进行编译,生成main.o,最后生成最终目标。第二次编译时,由于main.c没改变,自然main.o不需要改变,所以只需要执行all所对应的指令,生成目标main。

clean清除完环境后,再次make all,所有的操作都重新来过。

Makefile的源码如下:

all: main.ogcc main.o -o main
main.o: main.cgcc main.c -c -o main.o
clean:rm main.o main.exe

Scons编译

如下图所示是一个简易的scons编译行为,可以看到,脚本写的时候只需配置好源文件和目标文件source=main.c target=main,直接执行scons就可以,第二次编译的时候,由于什么都没变化scons不执行任何操作。

相比于make,清空环境只需要执行scons -c就可以自动清除编译生成的文件,无需像make要指定所有目标。而后再次编译又会重新开始了。

scons源码如下:

obj = Object('main.c')
Program('main', obj)

总结

gcc直接操作,每次编译都需要输入所有command,不能自动识别文件哪些有改动,只编译所需的文件,对于大型工程来说,编译时间还是很长的,只编译有修改的文件还是很方便的。

make解决了gcc的缺点,每次编译只需要执行make all,删除执行make clean就可以,但是写makefile的人需要知晓所有的中间文件,并了解文件的依赖关系。

scons保留了make的优点,每次编译只需要执行scons,删除执行scons -c就可以。同时写Sconstruct也更为轻松,不需要了解中间文件是什么,也不用专门写一个clean操作。所有的事情交给scons就行。

带.h文件的构建

gcc编译

多个文件编译的时候,先将2个文件生成object文件,再将2个object文件编译成目标main.exe。

main.c的源码如下:

#include <stdio.h>
#include "test.h"
int main()
{printf("hello, world\n");test_work();return 0;
}

test.c的源码如下:

#include <stdio.h>
#include "test.h"
void test_work(void)
{printf(TEST_PRINT_STRING);
}

test.h的源码如下:

#ifndef _TEST_H_
#define _TEST_H_#define TEST_PRINT_STRING "Test_Origin_0"
void test_work(void);#endif //_TEST_H_

make编译

按照流程写好,这里需要注意的是,修改完test.h后,再次编译的时候make只再次生成了main.exe,并没有重新编译test.omain.o,并没有达到我们的目标。

这是因为我们的makefile没写好,没把test.h依赖关系给写好。

将makefile修改一下,这时候再次修改test.h文件,make就知道需要重新编译文件了(大型项目通常通过-MMD -MP来生成.h的依赖树)。

注意,这个问题是我们写makefile经常遇到的问题,这个还存在更换之类的场景下,依赖关系没写好,导致再次编译的时候没将所有改动给编译进去,通常要先make clean再make all,完全丧失了make只编译改动的特点,导致编译效率大大降低。

Makefile的源码如下:

all: main.o test.ogcc main.o test.o -o main
main.o: main.c test.hgcc main.c -c -o main.o
test.o: test.c test.hgcc test.c -c -o test.o
clean:rm main.o test.o main.exe

Scons编译

scons的脚本依然很简单,配置好objects所需的源文件,在配置好目标,执行scons就可以了。需要注意的是,我们并没有显示的声明test.h的依赖关系,当修改test.h后再次编译,scons就能知道文件间的依赖关系,将整个工程正确的重新编译了,这对于写Sconstruct的人要求大大降低。

SConstruct源码如下:

objs = Object('main.c')
objs += Object('test.c')
Program('main', objs)

总结

在包含.h文件等多种隐示依赖的情况下,scons无需开发人员了解编译的底层原理,只需要将所需编译的主文件配置好,并配置好目标,剩下scons都可以自动搞定,大大节约了工程人员的开发和维护成本。

Scons原理分析

从上一章的分析可以看出,scons保留了make的所有优点,并简化了脚本编译,同时很好的维护了各个文件的隐示依赖关系,避免大家每次都要make clean,再make all了。

这一章我们简单分析下Scons是如何工作的。

脚本如何运行

没时间细看代码,自己也写过一些测试环境之类,大体猜测Scons的运行机制就是将Sconstruct文件用exec函数来执行,这样才能只写部分代码就可以完成编译工作。

简单搜索了下源码Sconstruct,可以看到_SConstruct_exists函数会去用这个文件。

之后有用python的exec函数来执行这个文件。

如何指导编译

如果学习过make的语法,其核心语法就是目标和依赖,利用好其基本语法,再借助其强大的工具函数,就可以完成项目的编译管理工作。

scons和make一样,本质并不会去做gcc的工作,更多是管理工程文件如何依次调用gcc进行编译

要用scons实现大型项目的管理,也必须理解其底层运行机制,这样才能实现各种复杂的行为。直接学习其源码是可以的,但是太累了,直接看其文档也能看到一些,但是也不是特别清楚,所以这里笔者以自己浅显的理解来分析其行为。

从上面的说明例子中,简单的c项目编译,只需要指定Object所需的source文件,然后再指定Program所需的objs和target对象,剩下都是scons来完成的。那么我们依次拆分其操作行为。

打印环境信息env.Dump()

scons所有的配置参数和信息存储在Environment对象中,可以通过Dump方法可以打印当前scons的所有配置参数,而后用python的print方法就可以打印出来了。

官方手册上也有说明:

env = Environment()
print(env.Dump())

打印出来的信息如下所示,参数有很多,看不懂咋办。

目前只需要完成嵌入式C开发需要,只要认下面几个关键字:

  • C编译:CCCCCOM

  • ASM编译:ASASCOM

  • 链接:LINKLINKCOM

scons: Reading SConscript files ...
{ 'AR': 'ar','ARCOM': '$AR $ARFLAGS $TARGET $SOURCES','ARFLAGS': ['rc'],'AS': 'as','ASCOM': '$AS $ASFLAGS -o $TARGET $SOURCES','ASFLAGS': [],'ASPPCOM': '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o ''$TARGET $SOURCES','ASPPFLAGS': '$ASFLAGS','BUILDERS': { 'CopyAs': <SCons.Builder.BuilderBase object at 0x000001F3D9B0C7F0>,'CopyTo': <SCons.Builder.BuilderBase object at 0x000001F3D9B0C7C0>,'JarFile': <SCons.Builder.BuilderBase object at 0x000001F3D9B0C910>,'JavaClassDir': <SCons.Builder.BuilderBase object at 0x000001F3D9B0CC10>,'JavaClassFile': <SCons.Builder.BuilderBase object at 0x000001F3D9B0CB50>,'JavaFile': <SCons.Builder.CompositeBuilder object at 0x000001F3D9B0CA60>,'Library': <SCons.Builder.BuilderBase object at 0x000001F3D9ACAA10>,'LoadableModule': <SCons.Builder.BuilderBase object at 0x000001F3D9ACA680>,'M4': <SCons.Builder.BuilderBase object at 0x000001F3D9B0C4C0>,'Object': <SCons.Builder.CompositeBuilder object at 0x000001F3D9AC9360>,'Program': <SCons.Builder.BuilderBase object at 0x000001F3D9AC9900>,'ProgramAllAtOnce': <SCons.Builder.BuilderBase object at 0x000001F3D9B0C6D0>,'RES': <SCons.Builder.BuilderBase object at 0x000001F3D9A2ECB0>,'RMIC': <SCons.Builder.BuilderBase object at 0x000001F3D9A89930>,'SharedLibrary': <SCons.Builder.BuilderBase object at 0x000001F3D9ACA290>,'SharedObject': <SCons.Builder.CompositeBuilder object at 0x000001F3D9AC95A0>,'StaticLibrary': <SCons.Builder.BuilderBase object at 0x000001F3D9ACAA10>,'StaticObject': <SCons.Builder.CompositeBuilder object at 0x000001F3D9AC9360>,'Substfile': <SCons.Builder.BuilderBase object at 0x000001F3D9AC8FD0>,'Tar': <SCons.Builder.BuilderBase object at 0x000001F3D9A8AB60>,'Textfile': <SCons.Builder.BuilderBase object at 0x000001F3D9AC8F70>,'Zip': <SCons.Builder.BuilderBase object at 0x000001F3D9A8AEC0>},'CC': 'gcc','CCCOM': '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES','CCDEPFLAGS': '-MMD -MF ${TARGET}.d','CCFLAGS': [],'CCVERSION': '12.1.0','CFILESUFFIX': '.c','CFLAGS': [],'CONFIGUREDIR': '#/.sconf_temp','CONFIGURELOG': '#/config.log','COPYSTR': 'Copy file(s): "$SOURCES" to "$TARGETS"','CPPDEFPREFIX': '-D','CPPDEFSUFFIX': '','CPPSUFFIXES': [ '.c','.C','.cxx','.cpp','.c++','.cc','.h','.H','.hxx','.hpp','.hh','.F','.fpp','.FPP','.m','.mm','.S','.spp','.SPP','.sx'],'CXX': 'g++','CXXCOM': '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM $SOURCES','CXXFILESUFFIX': '.cc','CXXFLAGS': [],'CXXVERSION': '12.1.0','DC': 'dmd','DCOM': '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET ''$SOURCES','DDEBUG': [],'DDEBUGPREFIX': '-debug=','DDEBUGSUFFIX': '','DFILESUFFIX': '.d','DFLAGPREFIX': '-','DFLAGS': [],'DFLAGSUFFIX': '','DINCPREFIX': '-I','DINCSUFFIX': '','DLIB': 'lib','DLIBCOM': '$DLIB $_DLIBFLAGS -c $TARGET $SOURCES $_DLIBFLAGS','DLIBDIRPREFIX': '-L-L','DLIBDIRSUFFIX': '','DLIBFLAGPREFIX': '-','DLIBFLAGSUFFIX': '','DLIBLINKPREFIX': '','DLIBLINKSUFFIX': '.lib','DLINK': '$DC','DLINKCOM': '$DLINK -of$TARGET $DLINKFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS ''$_DLIBFLAGS','DLINKFLAGS': [],'DPATH': ['#/'],'DRPATHPREFIX': '-L-rpath=','DRPATHSUFFIX': '','DSUFFIXES': ['.d'],'DVERPREFIX': '-version=','DVERSIONS': [],'DVERSUFFIX': '','Dir': <SCons.Defaults.Variable_Method_Caller object at 0x000001F3D93C82E0>,'Dirs': <SCons.Defaults.Variable_Method_Caller object at 0x000001F3D93C8370>,'ENV': { 'COMSPEC': 'C:\\Windows\\system32\\cmd.exe','PATH': 'C:\\Windows\\System32;C:\\msys64\\mingw32\\bin','PATHEXT': '.COM;.EXE;.BAT;.CMD','SystemDrive': 'C:','SystemRoot': 'C:\\Windows','TEMP': 'C:\\Users\\wenbo\\AppData\\Local\\Temp','TMP': 'C:\\Users\\wenbo\\AppData\\Local\\Temp','USERPROFILE': 'C:\\Users\\wenbo'},'ESCAPE': <function escape at 0x000001F3D9519B40>,'F03': 'gfortran','F03COM': '$F03 -o $TARGET -c $FORTRANCOMMONFLAGS $F03FLAGS $_F03INCFLAGS ''$_FORTRANMODFLAG $SOURCES','F03FLAGS': [],'F03PPCOM': '$F03 -o $TARGET -c $FORTRANCOMMONFLAGS $F03FLAGS $CPPFLAGS ''$_CPPDEFFLAGS $_F03INCFLAGS $_FORTRANMODFLAG $SOURCES','F08': 'gfortran','F08COM': '$F08 -o $TARGET -c $FORTRANCOMMONFLAGS $F08FLAGS $_F08INCFLAGS ''$_FORTRANMODFLAG $SOURCES','F08FLAGS': [],'F08PPCOM': '$F08 -o $TARGET -c $FORTRANCOMMONFLAGS $F08FLAGS $CPPFLAGS ''$_CPPDEFFLAGS $_F08INCFLAGS $_FORTRANMODFLAG $SOURCES','F77': 'gfortran','F77COM': '$F77 -o $TARGET -c $FORTRANCOMMONFLAGS $F77FLAGS $_F77INCFLAGS ''$SOURCES','F77FLAGS': [],'F77PPCOM': '$F77 -o $TARGET -c $FORTRANCOMMONFLAGS $F77FLAGS $CPPFLAGS ''$_CPPDEFFLAGS $_F77INCFLAGS $SOURCES','F90': 'gfortran','F90COM': '$F90 -o $TARGET -c $FORTRANCOMMONFLAGS $F90FLAGS $_F90INCFLAGS ''$_FORTRANMODFLAG $SOURCES','F90FLAGS': [],'F90PPCOM': '$F90 -o $TARGET -c $FORTRANCOMMONFLAGS $F90FLAGS $CPPFLAGS ''$_CPPDEFFLAGS $_F90INCFLAGS $_FORTRANMODFLAG $SOURCES','F95': 'gfortran','F95COM': '$F95 -o $TARGET -c $FORTRANCOMMONFLAGS $F95FLAGS $_F95INCFLAGS ''$_FORTRANMODFLAG $SOURCES','F95FLAGS': [],'F95PPCOM': '$F95 -o $TARGET -c $FORTRANCOMMONFLAGS $F95FLAGS $CPPFLAGS ''$_CPPDEFFLAGS $_F95INCFLAGS $_FORTRANMODFLAG $SOURCES','FORTRAN': 'gfortran','FORTRANCOM': '$FORTRAN -o $TARGET -c $FORTRANCOMMONFLAGS $FORTRANFLAGS ''$_FORTRANINCFLAGS $_FORTRANMODFLAG $SOURCES','FORTRANFLAGS': [],'FORTRANMODDIR': '','FORTRANMODDIRPREFIX': '-J','FORTRANMODDIRSUFFIX': '','FORTRANMODPREFIX': '','FORTRANMODSUFFIX': '.mod','FORTRANPPCOM': '$FORTRAN -o $TARGET -c $FORTRANCOMMONFLAGS $FORTRANFLAGS ''$CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS $_FORTRANMODFLAG ''$SOURCES','FORTRANSUFFIXES': [ '.f','.for','.ftn','.F','.FOR','.FTN','.fpp','.FPP','.f77','.F77','.f90','.F90','.f95','.F95','.f03','.F03','.f08','.F08'],'FRAMEWORKPATH': [],'FRAMEWORKS': [],'File': <SCons.Defaults.Variable_Method_Caller object at 0x000001F3D93C83A0>,'HOST_ARCH': 'x86_64','HOST_OS': 'win32','IDLSUFFIXES': ['.idl', '.IDL'],'IMPLIBNOVERSIONSYMLINKS': True,'INCF03PREFIX': '-I','INCF03SUFFIX': '','INCF08PREFIX': '-I','INCF08SUFFIX': '','INCF77PREFIX': '-I','INCF77SUFFIX': '','INCF90PREFIX': '-I','INCF90SUFFIX': '','INCF95PREFIX': '-I','INCF95SUFFIX': '','INCFORTRANPREFIX': '-I','INCFORTRANSUFFIX': '','INCPREFIX': '-I','INCSUFFIX': '','JAR': 'jar','JARCOM': "${TEMPFILE('$_JARCOM','$JARCOMSTR')}",'JARFLAGS': ['cf'],'JARSUFFIX': '.jar','JAVABOOTCLASSPATH': [],'JAVAC': 'javac','JAVACCOM': "${TEMPFILE('$_JAVACCOM','$JAVACCOMSTR')}",'JAVACFLAGS': [],'JAVACLASSPATH': [],'JAVACLASSSUFFIX': '.class','JAVAINCLUDES': [],'JAVASOURCEPATH': [],'JAVASUFFIX': '.java','LDMODULE': '$SHLINK','LDMODULECOM': <SCons.Action.CommandGeneratorAction object at 0x000001F3D9A2E800>,'LDMODULEEMITTER': [ <function lib_emitter at 0x000001F3D93B4AF0>,<function ldmod_symlink_emitter at 0x000001F3D9AE8940>,<function shlib_emitter at 0x000001F3D9A5A290>],'LDMODULEFLAGS': '$SHLINKFLAGS','LDMODULENAME': '${LDMODULEPREFIX}$_get_ldmodule_stem${_LDMODULESUFFIX}','LDMODULENOVERSIONSYMLINKS': True,'LDMODULEPREFIX': '$SHLIBPREFIX','LDMODULESUFFIX': '$SHLIBSUFFIX','LDMODULEVERSION': '$SHLIBVERSION','LDMODULE_NOVERSION_SYMLINK': '$_get_shlib_dir${LDMODULEPREFIX}$_get_ldmodule_stem${LDMODULESUFFIX}','LDMODULE_SONAME_SYMLINK': '$_get_shlib_dir$_LDMODULESONAME','LIBDIRPREFIX': '-L','LIBDIRSUFFIX': '','LIBLINKPREFIX': '-l','LIBLINKSUFFIX': '','LIBPREFIX': 'lib','LIBPREFIXES': ['$LIBPREFIX'],'LIBSUFFIX': '.a','LIBSUFFIXES': ['$LIBSUFFIX'],'LINESEPARATOR': '\n','LINK': '$SMARTLINK','LINKCOM': '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS ''$_LIBFLAGS','LINKFLAGS': [],'M4': 'm4','M4COM': 'cd ${SOURCE.rsrcdir} && $M4 $M4FLAGS < ${SOURCE.file} > ''${TARGET.abspath}','M4FLAGS': ['-E'],'MAXLINELENGTH': 2048,'NINJA_DEPFILE_PARSE_FORMAT': 'gcc','OBJPREFIX': '','OBJSUFFIX': '.o','PLATFORM': 'win32','PROGPREFIX': '','PROGSUFFIX': '.exe','PSPAWN': <function piped_spawn at 0x000001F3D9519990>,'RANLIB': 'ranlib','RANLIBCOM': '$RANLIB $RANLIBFLAGS $TARGET','RANLIBFLAGS': [],'RC': 'windres','RCCOM': '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX} ${SOURCE.dir} ''$RCFLAGS -i $SOURCE -o $TARGET','RCFLAGS': [],'RCINCFLAGS': '${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, ''TARGET, SOURCE, affect_signature=False)}','RCINCPREFIX': '--include-dir ','RCINCSUFFIX': '','RDirs': <SCons.Defaults.Variable_Method_Caller object at 0x000001F3D93C8430>,'RMIC': 'rmic','RMICCOM': '$RMIC $RMICFLAGS -d ${TARGET.attributes.java_lookupdir} ''-classpath ${SOURCE.attributes.java_classdir} ''${SOURCES.attributes.java_classname}','RMICFLAGS': [],'RPATHPREFIX': '-Wl,-rpath=','RPATHSUFFIX': '','SCANNERS': [<SCons.Scanner.ScannerBase object at 0x000001F3D9393C10>],'SHCC': '$CC','SHCCCOM': '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES','SHCCFLAGS': ['$CCFLAGS'],'SHCFLAGS': ['$CFLAGS'],'SHCXX': '$CXX','SHCXXCOM': '$SHCXX -o $TARGET -c $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES','SHCXXFLAGS': ['$CXXFLAGS'],'SHDC': '$DC','SHDCOM': '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -fPIC ''-of$TARGET $SOURCES','SHDLINK': '$DC','SHDLINKCOM': '$DLINK -of$TARGET $SHDLINKFLAGS $__SHDLIBVERSIONFLAGS ''$__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS','SHDLINKFLAGS': ['$DLINKFLAGS', '-shared', '-defaultlib=libphobos2.so'],'SHELL': 'C:\\Windows\\System32\\cmd.exe','SHF03': '$F03','SHF03COM': '$SHF03 -o $TARGET -c $FORTRANCOMMONFLAGS $SHF03FLAGS ''$_F03INCFLAGS $_FORTRANMODFLAG $SOURCES','SHF03FLAGS': ['$F03FLAGS'],'SHF03PPCOM': '$SHF03 -o $TARGET -c $FORTRANCOMMONFLAGS $SHF03FLAGS ''$CPPFLAGS $_CPPDEFFLAGS $_F03INCFLAGS $_FORTRANMODFLAG ''$SOURCES','SHF08': '$F08','SHF08COM': '$SHF08 -o $TARGET -c $FORTRANCOMMONFLAGS $SHF08FLAGS ''$_F08INCFLAGS $_FORTRANMODFLAG $SOURCES','SHF08FLAGS': ['$F08FLAGS'],'SHF08PPCOM': '$SHF08 -o $TARGET -c $FORTRANCOMMONFLAGS $SHF08FLAGS ''$CPPFLAGS $_CPPDEFFLAGS $_F08INCFLAGS $_FORTRANMODFLAG ''$SOURCES','SHF77': '$F77','SHF77COM': '$SHF77 -o $TARGET -c $FORTRANCOMMONFLAGS $SHF77FLAGS ''$_F77INCFLAGS $SOURCES','SHF77FLAGS': ['$F77FLAGS'],'SHF77PPCOM': '$SHF77 -o $TARGET -c $FORTRANCOMMONFLAGS $SHF77FLAGS ''$CPPFLAGS $_CPPDEFFLAGS $_F77INCFLAGS $SOURCES','SHF90': '$F90','SHF90COM': '$SHF90 -o $TARGET -c $FORTRANCOMMONFLAGS $SHF90FLAGS ''$_F90INCFLAGS $_FORTRANMODFLAG $SOURCES','SHF90FLAGS': ['$F90FLAGS'],'SHF90PPCOM': '$SHF90 -o $TARGET -c $FORTRANCOMMONFLAGS $SHF90FLAGS ''$CPPFLAGS $_CPPDEFFLAGS $_F90INCFLAGS $_FORTRANMODFLAG ''$SOURCES','SHF95': '$F95','SHF95COM': '$SHF95 -o $TARGET -c $FORTRANCOMMONFLAGS $SHF95FLAGS ''$_F95INCFLAGS $_FORTRANMODFLAG $SOURCES','SHF95FLAGS': ['$F95FLAGS'],'SHF95PPCOM': '$SHF95 -o $TARGET -c $FORTRANCOMMONFLAGS $SHF95FLAGS ''$CPPFLAGS $_CPPDEFFLAGS $_F95INCFLAGS $_FORTRANMODFLAG ''$SOURCES','SHFORTRAN': '$FORTRAN','SHFORTRANCOM': '$SHFORTRAN -o $TARGET -c $FORTRANCOMMONFLAGS ''$SHFORTRANFLAGS $_FORTRANINCFLAGS $_FORTRANMODFLAG $SOURCES','SHFORTRANFLAGS': ['$FORTRANFLAGS'],'SHFORTRANPPCOM': '$SHFORTRAN -o $TARGET -c $FORTRANCOMMONFLAGS ''$SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS ''$_FORTRANMODFLAG $SOURCES','SHLIBEMITTER': [ <function lib_emitter at 0x000001F3D93B4AF0>,<function shlib_symlink_emitter at 0x000001F3D9AE8B80>,<function shlib_emitter at 0x000001F3D9A5A290>],'SHLIBNAME': '${_get_shlib_dir}${SHLIBPREFIX}$_get_shlib_stem${_SHLIBSUFFIX}','SHLIBNOVERSIONSYMLINKS': True,'SHLIBPREFIX': '','SHLIBSONAMEFLAGS': '-Wl,-soname=$_SHLIBSONAME','SHLIBSUFFIX': '.dll','SHLIB_NOVERSION_SYMLINK': '${_get_shlib_dir}${SHLIBPREFIX}$_get_shlib_stem${SHLIBSUFFIX}','SHLIB_SONAME_SYMLINK': '${_get_shlib_dir}$_SHLIBSONAME','SHLINK': '$LINK','SHLINKCOM': <SCons.Action.CommandGeneratorAction object at 0x000001F3D9A2E7A0>,'SHLINKCOMSTR': <function shlib_generator at 0x000001F3D9A59630>,'SHLINKFLAGS': ['$LINKFLAGS', '-shared'],'SHOBJPREFIX': '$OBJPREFIX','SHOBJSUFFIX': '.o','SMARTLINK': <function smart_link at 0x000001F3D93B4A60>,'SPAWN': <function spawn at 0x000001F3D9519AB0>,'STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME': 1,'SUBSTFILEPREFIX': '','SUBSTFILESUFFIX': '','TAR': 'tar','TARCOM': '$TAR $TARFLAGS -f $TARGET $SOURCES','TARFLAGS': ['-c'],'TARGET_ARCH': None,'TARGET_OS': None,'TARSUFFIX': '.tar','TEMPFILE': <class 'SCons.Platform.TempFileMunge'>,'TEMPFILEARGESCFUNC': <function quote_spaces at 0x000001F3D9252DD0>,'TEMPFILEARGJOIN': ' ','TEMPFILEPREFIX': '@','TEXTFILEPREFIX': '','TEXTFILESUFFIX': '.txt','TOOLS': [ 'default','mingw','gcc','g++','gnulink','ar','gas','gfortran','m4','dmd','filesystem','jar','javac','rmic','tar','zip','textfile'],'WINDOWSDEFPREFIX': '','WINDOWSDEFSUFFIX': '.def','WIXCANDLE': 'candle.exe','WIXLIGHT': 'light.exe','ZIP': 'zip','ZIPCOM': <SCons.Action.FunctionAction object at 0x000001F3D9A8ACB0>,'ZIPCOMPRESSION': 8,'ZIPFLAGS': [],'ZIPROOT': [],'ZIPSUFFIX': '.zip','_CCCOMCOM': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS','_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, ''TARGET, SOURCE)}','_CPPINCFLAGS': '${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, ''TARGET, SOURCE, affect_signature=False)}','_DDEBUGFLAGS': '${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)}','_DFLAGS': '${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)}','_DINCFLAGS': '${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, ''TARGET, SOURCE)}','_DLIBDIRFLAGS': '${_concat(DLIBDIRPREFIX, LIBPATH, DLIBDIRSUFFIX, __env__, ''RDirs, TARGET, SOURCE)}','_DLIBFLAGS': '${_stripixes(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, ''LIBPREFIXES, LIBSUFFIXES,  __env__)}','_DRPATH': '${_concat(DRPATHPREFIX, RPATH, DRPATHSUFFIX, __env__)}','_DVERFLAGS': '${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)}','_F03INCFLAGS': '${_concat(INCF03PREFIX, F03PATH, INCF03SUFFIX, __env__, ''RDirs, TARGET, SOURCE, affect_signature=False)}','_F08INCFLAGS': '${_concat(INCF08PREFIX, F08PATH, INCF08SUFFIX, __env__, ''RDirs, TARGET, SOURCE, affect_signature=False)}','_F77INCFLAGS': '${_concat(INCF77PREFIX, F77PATH, INCF77SUFFIX, __env__, ''RDirs, TARGET, SOURCE, affect_signature=False)}','_F90INCFLAGS': '${_concat(INCF90PREFIX, F90PATH, INCF90SUFFIX, __env__, ''RDirs, TARGET, SOURCE, affect_signature=False)}','_F95INCFLAGS': '${_concat(INCF95PREFIX, F95PATH, INCF95SUFFIX, __env__, ''RDirs, TARGET, SOURCE, affect_signature=False)}','_FORTRANINCFLAGS': '${_concat(INCFORTRANPREFIX, FORTRANPATH, ''INCFORTRANSUFFIX, __env__, RDirs, TARGET, SOURCE, ''affect_signature=False)}','_FORTRANMODFLAG': '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, ''FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)','_JARCOM': '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARSOURCES','_JARFLAGS': <function jarFlags at 0x000001F3D9AB6EF0>,'_JARMANIFEST': <function jarManifest at 0x000001F3D9AB6E60>,'_JARSOURCES': <function jarSources at 0x000001F3D9AB5990>,'_JAVABOOTCLASSPATH': '${_javapathopt("-bootclasspath", ''"JAVABOOTCLASSPATH")} ','_JAVACCOM': '$JAVAC $JAVACFLAGS $_JAVABOOTCLASSPATH $_JAVACLASSPATH -d ''${TARGET.attributes.java_classdir} $_JAVASOURCEPATH $SOURCES','_JAVACLASSPATH': '${_javapathopt("-classpath", "JAVACLASSPATH")} ','_JAVASOURCEPATH': '${_javapathopt("-sourcepath", "JAVASOURCEPATH", ''"_JAVASOURCEPATHDEFAULT")} ','_JAVASOURCEPATHDEFAULT': '${TARGET.attributes.java_sourcedir}','_LDMODULESONAME': <function _ldmodule_soname at 0x000001F3D9AE9000>,'_LDMODULESOVERSION': <function _ldmodule_soversion at 0x000001F3D9AE8F70>,'_LDMODULESUFFIX': '${LDMODULESUFFIX}${_LDMODULEVERSION}','_LDMODULEVERSION': <function _LDMODULEVERSION at 0x000001F3D9AE9090>,'_LDMODULEVERSIONFLAGS': '$LDMODULEVERSIONFLAGS -Wl,-soname=$_LDMODULESONAME','_LIBDIRFLAGS': '${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, ''RDirs, TARGET, SOURCE, affect_signature=False)}','_LIBFLAGS': '${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, ''LIBSUFFIXES, __env__)}','_RPATH': '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}','_SHDLIBVERSIONFLAGS': '$SHDLIBVERSIONFLAGS -L-soname=$_SHLIBSONAME','_SHLIBSONAME': <function _soname at 0x000001F3D9AE8CA0>,'_SHLIBSOVERSION': <function _soversion at 0x000001F3D9AE8C10>,'_SHLIBSUFFIX': '$SHLIBSUFFIX','_SHLIBVERSION': "${SHLIBVERSION and '.'+SHLIBVERSION or ''}",'_SHLIBVERSIONFLAGS': '$SHLIBVERSIONFLAGS -Wl,-soname=$_SHLIBSONAME','__DSHLIBVERSIONFLAGS': '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}','__LDMODULEVERSIONFLAGS': '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}','__SHLIBVERSIONFLAGS': '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}','__lib_either_version_flag': <function __lib_either_version_flag at 0x000001F3D93B7250>,'__libversionflags': <function __libversionflags at 0x000001F3D93B6F80>,'_concat': <function _concat at 0x000001F3D93B6CB0>,'_defines': <function _defines at 0x000001F3D93B6EF0>,'_get_ldmodule_stem': <function _get_ldmodule_stem at 0x000001F3D9AE8EE0>,'_get_shlib_dir': <function _get_shlib_dir at 0x000001F3D9AE8DC0>,'_get_shlib_stem': <function _get_shlib_stem at 0x000001F3D9AE8D30>,'_javapathopt': <class 'SCons.Tool.javac.pathopt'>,'_stripixes': <function _stripixes at 0x000001F3D93B6DD0>}
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
scons: done building targets.

C文件编译行为分析

首先我们调整下编译行为,注释Program,只执行Object,可以看到其最终执行了gcc -o main.o -c main.c,那为什么其知道要使用这个命令呢?如果我需要加一些编译参数,应该如何配置呢?如果要进行嵌入式开发,需要替换gcc为aram-xx-gcc如何处理?

从上文可知,scons是通过CCCOM来指导c文件编译的。从官网上也可以看出是通过这个来完成对c的源文件编译,生成object文件。

通过dump可知,默认配置下,各个参数如下:

  • TARGET:就是目标文件
  • SOURCES:就是源文件列表
  • CFLAGS:通用C配置参数
  • CCFLAGS:通用C和C++公用的配置参数,因为scons支持多个语言,配置了这个参数,其同时会在C++编译时使用。详细可以参考:CXXCOM
  • _CCCOMCOM: 更多参数配置,如-D或者-I等配置。
    • CPPFLAGS:用于预处理相关的配置,既可以用于C编译,同时也被用于ASM编译。
    • _CPPDEFFLAGS:用于-D的预编译配置。
    • _CPPINCFLAGS:用于-I的include路径配置。
{'CC': 'gcc','CCCOM': '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES','CCFLAGS': [],'CFLAGS': [],'_CCCOMCOM': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS','CPPDEFPREFIX': '-D','CPPDEFSUFFIX': '','_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, ''TARGET, SOURCE)}','INCPREFIX': '-I','INCSUFFIX': '','_CPPINCFLAGS': '${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, ''TARGET, SOURCE, affect_signature=False)}',
}

其实熟悉GCC的基本已经看明白了,其实把gcc编译object的参数全部配置好了,需要的话直接改env对应的参数即可。

在示例中,obj = Object('main.c')中SOURCES = [main.c],TARGET = main.o,其他参数由于都没开启,所以没显示。最终就是我们看到的gcc操作。

gcc -o main.o -c main.c
obj = Object('main.c')

假设调整CCFLAGS,加入-g参数,编译结果如下图所示,可以看出确实是跟着变的,之后我们只要对应改相关的参数,就可以做到修改gcc的目的。

ASM文件编译行为分析

在PC环境下,基本很少写ASM相关的程序,而在嵌入式环境下,不管是startup文件还是一些特殊用途的行为,都需要使用ASM语句进行操作。

类似于C文件的编译过程,由于PC环境所需的汇编指令各不相同,在嵌入式系统下针对对应的芯片,也有不同汇编指令,本节只对相关配置进行说明。

从上文可知,scons是通过ASCOM来指导asm文件编译的。从官网上也可以看出是通过这个来完成对c的源文件编译,生成object文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gKSS5iUs-1663252258508)(C:/Users/wenbo/AppData/Roaming/Typora/typora-user-images/image-20220915091107811.png)]

通过dump可知,默认配置下,各个参数如下:

  • TARGET:就是目标文件
  • SOURCES:就是源文件列表
  • ASFLAGS:通用ASM配置参数
{'AS': 'as','ASCOM': '$AS $ASFLAGS -o $TARGET $SOURCES','ASFLAGS': [],
}

和gcc类似,实际是使用as来对ASM程序进行编译,生成object文件。

Link编译行为分析

在完成Object的文件编译后,生成了一个或多个的Object文件。编译的最后一步是将这些Object文件Link为Program程序。

我们保留生成的Object,只执行Program,

首先我们调整下编译行为,注释Program,只执行Object,可以看到其最终执行了gcc -o main.exe main.o,那为什么其知道要使用这个命令呢?如果我需要加一些编译参数,应该如何配置呢?如果要进行嵌入式开发,需要替换gcc为aram-xx-ld如何处理?

从上文可知,scons是通过LINKCOM来指导LINK的。从官网上也可以看出是通过这个来完成对Object文件Link流程的。

通过dump可知,默认配置下,各个参数如下,由于LINK实际环境使用的程序各不一样,所以实际是一个python对象:

  • TARGET:就是目标文件
  • SOURCES:就是源文件列表
  • LINKFLAGS:通用ld配置参数
  • _LIBDIRFLAGS:用于-L的lib路径配置,LIBPATH为具体路径
  • _LIBFLAGS: 用于-l的lib文件配置,LIBS为具体lib名称。
{'LINK': '$SMARTLINK','LINKCOM': '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS ''$_LIBFLAGS','LINKFLAGS': [],'LIBDIRPREFIX': '-L','LIBDIRSUFFIX': '','_LIBDIRFLAGS': '${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, ''RDirs, TARGET, SOURCE, affect_signature=False)}','LIBLINKPREFIX': '-l','LIBLINKSUFFIX': '','_LIBFLAGS': '${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, ''LIBSUFFIXES, __env__)}',
}

和GCC一样,Program最终调用的就是这个配置。

假设调整LINKFLAGS,加入-g参数,编译结果如下图所示,可以看出确实是跟着变的,之后我们只要对应改相关的参数,就可以做到修改link的目的。

嵌入式开发和Scons模板

默认情况下,c编译是根据具体编译环境来的,scons会自己选定gcc程序、asm程序和Link程序,但是在嵌入式开发时,需要指定交叉编译器,按照之前的所描述的,其实大家已经清楚了,只需要调整相应的配置参数即可。

此外嵌入式开发也有一些特殊的配置,这都需要我们理解并调整配置参数才行。

如下所示,每行都有注释,下面对一些关键参数/函数进行说明:

  • get_path_files:工具函数,获取路径下所有特定后缀的文件,当然可以用其他方式实现(实现这个函数主要为了写path的时候方便)
  • GCC_ARM_PATH:交叉工具链路径,scons直接改工作路径不怎么会,直接指定路径
  • PLF:编译平台,默认使用PC编译,当然嵌入式开发可以选'arm',之后会自动去配置相关参数
  • PREFIX:嵌入式开发需要,交叉工具链前缀
  • SPEC_LD_FLAGS:arm交叉工具链开发的时候,LD使用有一些特殊的要求,scons原生的参数不怎么好用,需要在_LIBFLAGS参数尾巴拼接上Map信息,其他工具链有特殊要求也可以根据需要调整
import osdef get_path_files(dirs, file_ext):path_files = []# print('dir: ' + str(dirs))for dir in dirs:# print('dir: ' + dir)path_files.append(Glob(dir + '/' + file_ext))return path_filesGCC_ARM_PATH = 'D:/env/gcc-arm-none-eabi-4_8-2014q3-20140805-win32'#PLF = 'arm'
PLF = ''# toolchains
if PLF == 'arm':PREFIX = GCC_ARM_PATH + '/bin/arm-none-eabi-'
else:PREFIX = ''CC = PREFIX + 'gcc'
AS = PREFIX + 'as'
AR = PREFIX + 'ar'
CXX = PREFIX + 'g++'
LINK = PREFIX + 'ld'SIZE = PREFIX + 'size'
OBJDUMP = PREFIX + 'objdump'
OBJCPY = PREFIX + 'objcopy'TARGET_PATH = 'build/'
TARGET_NAME = 'main'
TARGET_WITHOUT_SUFFIX = TARGET_PATH + TARGET_NAME###################################################################################
# C source dirs config
C_DIRS = []
#C_DIRS.append('src')# C source files config
C_FILES = []
#C_FILES.append('main.c')# Create c sources list
C_SRC_LIST = get_path_files(C_DIRS, '*.c') + C_FILES###################################################################################
# ASM source dirs config
AS_DIRS = []
#AS_DIRS.append('src')# ASM source files config
AS_FILES = []
#AS_FILES.append('startup.s')AS_SRC_LIST = get_path_files(AS_DIRS, '*.s') + AS_FILES###################################################################################
# -I, Include path config
CPP_PATH = []
#CPP_PATH.append('inc')# -D, Preprocess Define
CPP_DEFINES = []
#CPP_DEFINES.append('CFG_TEST')# C generate define
C_FLAGS = []
C_FLAGS.append('-O1')
C_FLAGS.append('-g')
C_FLAGS.append('-std=c99')# C and C++ generate define
CC_FLAGS = []
CC_FLAGS.append('-Wall')# ASM generate define
AS_FLAGS = []
AS_FLAGS.append('-g')# Link Config
LINK_FLAGS = []
#LINK_FLAGS.append('-Wl,–gc-sections')# lib path.
LIB_PATH = []
#LIB_PATH.append('lib')# .lib, .a file
LIBS_FILES = []
#LIBS_FILES.append('test')# spec ld flag. Arm spec.
SPEC_LD_FLAGS = []
if PLF == 'arm':SPEC_LD_FLAGS.append('-Map')SPEC_LD_FLAGS.append(TARGET_WITHOUT_SUFFIX + '.map')SPEC_LD_FLAGS.append('-T' + 'src/map_ram.txt')env = Environment()###################################################################################
# Step0: toolchains setting.
if PLF == 'arm':env['CC'] = CCenv['AS'] = ASenv['AR'] = ARenv['CXX'] = CXXenv['LINK'] = LINKenv['OBJSUFFIX'] = '.o'env['LIBPREFIX'] = 'lib'env['LIBSUFFIX'] = '.a'env['PROGSUFFIX'] = '.elf'###################################################################################
# Step1: C compile setting. use <print(env.Dump())> for details.
# 'CCCOM': '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'# Step1.0: General options, like: optim/debug setting. $CFLAGS.
env.Append(CFLAGS=C_FLAGS)# Step1.1: General options, other setting. $CCFLAGS.
env.Append(CCFLAGS=CC_FLAGS)# Step1.2: -D, -I, setting. $_CCCOMCOM
# '_CCCOMCOM': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS'
# StepX.2.0: CPPFLAGS setting. --- do nothing.
# StepX.2.1: -D setting.
# 'CPPDEFPREFIX': '-D'
# '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, '
#                 'TARGET, SOURCE)}',
env.Append(CPPDEFINES=CPP_DEFINES)
# Step1.2.2: -I setting.
# 'INCPREFIX': '-I'
# '_CPPINCFLAGS': '${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, '
#                 'TARGET, SOURCE, affect_signature=False)}',
env.Append(CPPPATH=CPP_PATH)###################################################################################
# Step2: ASM compile setting. use <print(env.Dump())> for details.
# 'ASCOM': '$AS $ASFLAGS -o $TARGET $SOURCES'# Step2.0: General options. $ASFLAGS.
env.Append(ASFLAGS=AS_FLAGS)###################################################################################
# Step3: LINK setting. use <print(env.Dump())> for details.
# 'LINKCOM': '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS '
#            '$_LIBFLAGS',# Step3.0: General options. $LINKFLAGS.
env.Append(LINKFLAGS=LINK_FLAGS)# Step3.1: Link path setting. $_LIBDIRFLAGS.
# '_LIBDIRFLAGS': '${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, '
#                 'RDirs, TARGET, SOURCE, affect_signature=False)}',
env.Append(LIBPATH=LIB_PATH)# Step3.2: libs setting, like *.a, *.lib. $_LIBFLAGS.
#   '_LIBFLAGS': '${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, '
#                'LIBSUFFIXES, __env__)}',
env.Append(LIBS=LIBS_FILES)# Step3.3: Add some spec params. must append at end.
env.Append(_LIBFLAGS=SPEC_LD_FLAGS)###################################################################################
# Step4: Compile Object files, use:
#        1. <$CCCOM>: For c code compile
#        2. <$ASCOM>: For asm code compile
c_objs = env.Object(C_SRC_LIST)
as_objs = env.Object(AS_SRC_LIST)###################################################################################
# Step5: Compile target <.elf>, use <$LINKCOM>.
target = env.Program(target = TARGET_WITHOUT_SUFFIX, source=[c_objs, as_objs])# Other compile target.
env.Command(TARGET_WITHOUT_SUFFIX + '.bin', target, OBJCPY + ' -v -O binary $SOURCE $TARGET')
env.Command(TARGET_WITHOUT_SUFFIX + '.lst', target, OBJDUMP + ' --source --all-headers --demangle --line-numbers --wide $SOURCE > $TARGET')
env.Command(TARGET_WITHOUT_SUFFIX + '.size', target, SIZE + ' --format=berkeley $SOURCE')# Dump() env params, if need.
#print(env.Dump())

模板使用示例

下面举一个例子,还是以PC举例,嵌入式开发自己根据需要配置即可。

有如下的一个多文件结构的C代码,目前要用scons来编译,在根目录下写SConstruct配置文件。

最终的SConstruct配置如下:

import osdef get_path_files(dirs, file_ext):path_files = []# print('dir: ' + str(dirs))for dir in dirs:# print('dir: ' + dir)path_files.append(Glob(dir + '/' + file_ext))return path_filesGCC_ARM_PATH = 'D:/env/gcc-arm-none-eabi-4_8-2014q3-20140805-win32'#PLF = 'arm'
PLF = ''# toolchains
if PLF == 'arm':PREFIX = GCC_ARM_PATH + '/bin/arm-none-eabi-'
else:PREFIX = ''CC = PREFIX + 'gcc'
AS = PREFIX + 'as'
AR = PREFIX + 'ar'
CXX = PREFIX + 'g++'
LINK = PREFIX + 'ld'SIZE = PREFIX + 'size'
OBJDUMP = PREFIX + 'objdump'
OBJCPY = PREFIX + 'objcopy'TARGET_PATH = 'build/'
TARGET_NAME = 'main'
TARGET_WITHOUT_SUFFIX = TARGET_PATH + TARGET_NAME###################################################################################
# C source dirs config
C_DIRS = []
C_DIRS.append('app\driver\src')
C_DIRS.append('module1\src')
C_DIRS.append('module2\src')# C source files config
C_FILES = []
C_FILES.append('app\main.c')# Create c sources list
C_SRC_LIST = get_path_files(C_DIRS, '*.c') + C_FILES###################################################################################
# ASM source dirs config
AS_DIRS = []
#AS_DIRS.append('src')# ASM source files config
AS_FILES = []
#AS_FILES.append('startup.s')AS_SRC_LIST = get_path_files(AS_DIRS, '*.s') + AS_FILES###################################################################################
# -I, Include path config
CPP_PATH = []
CPP_PATH.append('app\driver\inc')
CPP_PATH.append('module1\inc')
CPP_PATH.append('module2\inc')# -D, Preprocess Define
CPP_DEFINES = []
#CPP_DEFINES.append('CFG_TEST')# C generate define
C_FLAGS = []
C_FLAGS.append('-O1')
C_FLAGS.append('-g')
C_FLAGS.append('-std=c99')# C and C++ generate define
CC_FLAGS = []
CC_FLAGS.append('-Wall')# ASM generate define
AS_FLAGS = []
AS_FLAGS.append('-g')# Link Config
LINK_FLAGS = []
#LINK_FLAGS.append('-Wl,–gc-sections')# lib path.
LIB_PATH = []
#LIB_PATH.append('lib')# .lib, .a file
LIBS_FILES = []
#LIBS_FILES.append('test')# spec ld flag. Arm spec.
SPEC_LD_FLAGS = []
if PLF == 'arm':SPEC_LD_FLAGS.append('-Map')SPEC_LD_FLAGS.append(TARGET_WITHOUT_SUFFIX + '.map')SPEC_LD_FLAGS.append('-T' + 'src/map_ram.txt')env = Environment()###################################################################################
# Step0: toolchains setting.
if PLF == 'arm':env['CC'] = CCenv['AS'] = ASenv['AR'] = ARenv['CXX'] = CXXenv['LINK'] = LINKenv['OBJSUFFIX'] = '.o'env['LIBPREFIX'] = 'lib'env['LIBSUFFIX'] = '.a'env['PROGSUFFIX'] = '.elf'###################################################################################
# Step1: C compile setting. use <print(env.Dump())> for details.
# 'CCCOM': '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'# Step1.0: General options, like: optim/debug setting. $CFLAGS.
env.Append(CFLAGS=C_FLAGS)# Step1.1: General options, other setting. $CCFLAGS.
env.Append(CCFLAGS=CC_FLAGS)# Step1.2: -D, -I, setting. $_CCCOMCOM
# '_CCCOMCOM': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS'
# StepX.2.0: CPPFLAGS setting. --- do nothing.
# StepX.2.1: -D setting.
# 'CPPDEFPREFIX': '-D'
# '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, '
#                 'TARGET, SOURCE)}',
env.Append(CPPDEFINES=CPP_DEFINES)
# Step1.2.2: -I setting.
# 'INCPREFIX': '-I'
# '_CPPINCFLAGS': '${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, '
#                 'TARGET, SOURCE, affect_signature=False)}',
env.Append(CPPPATH=CPP_PATH)###################################################################################
# Step2: ASM compile setting. use <print(env.Dump())> for details.
# 'ASCOM': '$AS $ASFLAGS -o $TARGET $SOURCES'# Step2.0: General options. $ASFLAGS.
env.Append(ASFLAGS=AS_FLAGS)###################################################################################
# Step3: LINK setting. use <print(env.Dump())> for details.
# 'LINKCOM': '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS '
#            '$_LIBFLAGS',# Step3.0: General options. $LINKFLAGS.
env.Append(LINKFLAGS=LINK_FLAGS)# Step3.1: Link path setting. $_LIBDIRFLAGS.
# '_LIBDIRFLAGS': '${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, '
#                 'RDirs, TARGET, SOURCE, affect_signature=False)}',
env.Append(LIBPATH=LIB_PATH)# Step3.2: libs setting, like *.a, *.lib. $_LIBFLAGS.
#   '_LIBFLAGS': '${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, '
#                'LIBSUFFIXES, __env__)}',
env.Append(LIBS=LIBS_FILES)# Step3.3: Add some spec params. must append at end.
env.Append(_LIBFLAGS=SPEC_LD_FLAGS)###################################################################################
# Step4: Compile Object files, use:
#        1. <$CCCOM>: For c code compile
#        2. <$ASCOM>: For asm code compile
c_objs = env.Object(C_SRC_LIST)
as_objs = env.Object(AS_SRC_LIST)###################################################################################
# Step5: Compile target <.elf>, use <$LINKCOM>.
target = env.Program(target = TARGET_WITHOUT_SUFFIX, source=[c_objs, as_objs])# Other compile target.
env.Command(TARGET_WITHOUT_SUFFIX + '.bin', target, OBJCPY + ' -v -O binary $SOURCE $TARGET')
env.Command(TARGET_WITHOUT_SUFFIX + '.lst', target, OBJDUMP + ' --source --all-headers --demangle --line-numbers --wide $SOURCE > $TARGET')
env.Command(TARGET_WITHOUT_SUFFIX + '.size', target, SIZE + ' --format=berkeley $SOURCE')# Dump() env params, if need.
#print(env.Dump())

执行scons操作,可以看到相关参数的编译过程,最后也会生成bin、lst以及code size信息。

D:\test\sample_4>scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o app\driver\src\driver1.1.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc app\driver\src\driver1.1.c
gcc -o app\driver\src\driver2.2.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc app\driver\src\driver2.2.c
gcc -o app\main.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc app\main.c
gcc -o module1\src\module1.1.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc module1\src\module1.1.c
gcc -o module1\src\module1.2.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc module1\src\module1.2.c
gcc -o module2\src\module2.1.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc module2\src\module2.1.c
gcc -o module2\src\module2.2.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc module2\src\module2.2.c
gcc -o build\main.exe app\driver\src\driver1.1.o app\driver\src\driver2.2.o module1\src\module1.1.o module1\src\module1.2.o module2\src\module2.1.o module2\src\module2.2.o app\main.o
objcopy -v -O binary build\main.exe build\main.bin
copy from `build\main.exe' [pei-i386] to `build\main.bin' [binary]
objdump --source --all-headers --demangle --line-numbers --wide build\main.exe > build\main.lst
size --format=berkeley build\main.exetext    data     bss     dec     hex filename39620    2944    2676   45240    b0b8 build\main.exe
scons: done building targets.D:\test\sample_4>scons -c
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Cleaning targets ...
Removed app\driver\src\driver1.1.o
Removed app\driver\src\driver2.2.o
Removed app\main.o
Removed module1\src\module1.1.o
Removed module1\src\module1.2.o
Removed module2\src\module2.1.o
Removed module2\src\module2.2.o
Removed build\main.exe
Removed build\main.bin
Removed build\main.lst
scons: done cleaning targets.

转自:Scons环境搭建和编译原理概述及嵌入式开发常用模板_CoderBob的博客-CSDN博客_scons源码解析

Scons环境搭建和编译原理概述及嵌入式开发常用模板相关推荐

  1. 高通Android智能平台环境搭建_编译流程分析

    高通Android智能平台环境搭建_编译流程分析 高通平台环境搭建,编译,系统引导流程分析 TOC \o \h \z \u 1. 高通平台android开发总结. 7 1.1 搭建高通平台环境开发环境 ...

  2. FMT(基于rtthread)开源代码编译 win10下编译环境搭建及编译

    FMT(基于rtthread)开源代码编译 win10下编译环境搭建及编译 什么是Firmament? Firmament (FMT) 是一款基于模型设计 (Model-Based-Design, M ...

  3. -i 可以编译添加多个_大咖说 | 基于 NXP i.MX8 eIQ 环境搭建和编译

    一.前言 NXP 针对 Machine Learning ( ML ) 创建了 eIQ 工具,以方便开发者在 i.MX 系列设备上开发 ML 相关应用. 本篇将会分两个章节,一章节介绍 eIQ 软件, ...

  4. 高通平台开发环境搭建、编译、烧录(android5.1以上系统)

    高通平台开发环境搭建.编译.烧录(android5.1以上系统) 以MSN8937为例 1. 安装Ubuntu(12.04LTS以上稳定版本)     安装过程中必须以根用户登录或使用sudo获取ro ...

  5. MTK6795环境搭建与编译

    一. MTK6795介绍 说起MTK6795,已经有很多手机厂商用它来做他们旗舰级的处理器,比如说小米,魅族,乐视等.MT6795 采用八颗 Cortex A53,提供 2.2GHz 的时脉速度并提供 ...

  6. 高通平台环境搭建,编译,系统引导流程分析 .

    1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通开发板上烧录文件系统 建立高通平台开发环境 高通平台,android和 modem 编译流程分析 高通平台 7620 启动流 ...

  7. LNMP环境搭建之编译安装指南(php-5.3.27.tar.gz)

    测试环境:CentOS release 6.5 (Final) 软件安装:nginx   mysql-5.5.32-linux2.6-x86_64.tar.gz   php-5.3.27.tar.gz ...

  8. LAMP环境搭建之编译安装指南(php-5.3.27.tar.gz)

    测试环境:CentOS release 6.5 (Final) 软件安装:httpd-2.2.27.tar.gz   mysql-5.1.72.tar.gz   php-5.3.27.tar.gz 1 ...

  9. OSG+VS2010+win7环境搭建---OsgEarth编译

    OSG+VS2010+win7环境搭建 Win7下 osg+vs2010环境搭建 一.相关准备 a) Osg源码 当前最新版:OpenSceneGraph的3.0.0.zip 下载链接: http:/ ...

最新文章

  1. 观察者模式(Observer Pattern)(二):HeadFirst中的气象站的实现
  2. CCNP自学之路——eigrp--1
  3. STM32开发 -- 低功耗模式详解(3)
  4. SDNU 1427.分解质因数(水题)
  5. jdbc连接sqlserver,mysql,oracle
  6. win32diskimager报错:An error occured when attempting to XXX, Error 5: Access is Denied
  7. 一个标签的72变,打造一个纯CSS图标库
  8. 17.3.13--python编码问题
  9. 关于C# 模仿千千静听 磁铁窗体 的个人的一个思路
  10. 编译OpenJDK12:Only bundled freetype can be specified on Mac and Windows
  11. 1块钱的路由器,10亿美金的小米网络
  12. 用计算机刻盘,电脑怎么刻盘_怎么将电脑桌面的文件刻录到cd光盘
  13. 动态爱心表白代码(绝对炫酷)
  14. 百度、高德、谷歌、火星、wgs84(2000)地图坐标相互转换的JS实现
  15. git 记住账号密码、忽略部分文件、合并分支、将远程分支拉取到本地
  16. python多张子图画在一张画布上,显示图例和标题,并保存。
  17. 【毕业设计】酒店评价情感倾向分析系统 - python 深度学习
  18. 打造生产工具(1)——安装纯净windows系统
  19. 高通SDX12平台:启动流程梳理
  20. matlab用牛顿差值计算三次差值多项式,计算方法用Newton插值多项式求函数的近似值.docx...

热门文章

  1. 有多少爱 就有多少原谅
  2. (转)基于L297和L298的步进电机驱动器的设计
  3. 主板噪音测试软件,HiFi能如愿以偿?编辑揭秘板载声卡真相
  4. 如何在Hi3559A上运行自己的YOLOv3模型(一)
  5. 奇虎360选择IPO “壳概念”很受伤
  6. Android蓝牙技术+Demo
  7. Linux下的UDP通信
  8. 监听微信手Q的挂起事件(visibilitychange)
  9. SLAM入坑之一:用realsense D415跑一下ORB-SLAM在ROS-melodic上
  10. 读书笔记--《BIM手册》--案例笔记