原创作者: sofire   阅读:6743次   评论:0条   更新时间:2011-05-26    
Dmd编译器学习笔记

英文原文在这里:
http://digitalmars.com/d/dcompiler.html
在这里有一篇翻译文章:
http://sofire.iteye.com/blog/111667
不过,主要是关于windows的;我更关心Linux下的使用。
顺便看看两者有啥区别。

相关文件
注意:

Linux的dmd配置文件是dmd.conf
Windows的配置文件是sc.ini


  • /dmd/bin/dmd
  •     D 编译器的可执行文件
  • /dmd/bin/dumpobj
  •     Elf file dumper
  • /dmd/bin/obj2asm
  •     Elf文件反汇编器
  • /dmd/bin/dmd.conf
  •     全局配置文件(复制到 /etc/dmd.conf)
  • /dmd/lib/libphobos.a
  •     D运行库(复制到 /usr/lib/libphobos.a)


DMD的安装
[list=1]
  • 下载dmd程序:http://ftp.digitalmars.com/dmd.zip,解压到~/dmd目录
  • 复制dmd.conf文件到/etc目录
  • cp ~/dmd/bin/dmd.conf /etc
    
  • 给下面的文件添加执行权限
  • chmod u+x ~/dmd/bin/{dmd,dumpobj,obj2asm,rdmd}
    
  • 把~/dmd/bin添加到PATH环境变量;或者把它们复制到/usr/local/bin目录下(不要只复制可执行程序)
  • 复制库文件到/usr/lib目录
  • cp ~/dmd/lib/libphobos.a /usr/lib
    

    [/list]

    以上安装过程比较简单,只有PATH环境变量设置正确了,就应该没有什么问题

    编译参数和开关
    命令的格式:
    dmd   files...  -switches... 
    

    [Windows]支持以下类型的文件:
    Extension      File Type  
    none           D source files  
    .d             D source files  
    .di            D interface files  
    .obj           Object files to link in  
    .lib           Object code libraries to search  
    .exe           Name output executable file  
    .def           module definition file  
    .res           resource file  
    

    [Linux]支持以下类型的文件:
    Extension      File Type
    none  	       D source files
    .d 	           D source files
    .di 	       D interface files
    .o 	           Object files to link in
    .a 	           Library files to link in
    

    好像不支持.so文件--这一点不肯定

    编译开关之一
    -debug
        编译调试代码
    
    -debug=level
        编译调试代码:code <= level
    
    -debug=ident
        编译调试代码:标识符为ident
    
    -version=level
        生成版本代码:>=level
    
    -version=ident
        生成版本代码:==ident
        
    -unittest
        编译单元测试代码(还有断言)
    
    -cov
        添加覆盖率分析指令;运行程序后,会生成.lst文件
    
    -release
        生成发行版本;会去掉契约和断言等信息
    


    -debug / -version
    debug、version的使用方法很相似
    //debug.d
    import std.stdio;
    
    void main()
    {
        debug    { writefln("debug"); }
        debug(1) { writefln("debug(1)"); }
        debug(2) { writefln("debug(2)"); }
    
        debug(ERROR) { writefln("debug(ERROR)"); }
        debug(WARN)  { writefln("debug(WARN)"); }
    
        version(HOME)      { writefln("version(HOME)"); }
        version(BUSINESS)  { writefln("version(BUSINESS)"); }
    
        version(WINDOWS) {} else { writefln("version(!WINDOWS)"); }
    }
    

    编译并运行之:
    # dmd -debug -run debug.d
    debug
    debug(1)
    version(!WINDOWS)
    
    # dmd -debug=1 -run debug.d
    debug
    debug(1)
    version(!WINDOWS)
    
    # dmd -debug=2 -run debug.d
    debug
    debug(1)
    debug(2)
    version(!WINDOWS)
    
    # dmd -debug=ERROR -run debug.d
    debug(ERROR)
    version(!WINDOWS)
    
    # dmd -debug=WARN -run debug.d
    debug(WARN)
    version(!WINDOWS)
    
    # dmd -version=HOME -run debug.d
    version(HOME)
    version(!WINDOWS)
    
    # dmd -version=BUSINESS -version=WINDOWS -run debug.d
    version(BUSINESS)
    


    -unittest
    //unittest.d
    import std.stdio;
    
    class A
    {
        int i;
        this(int v) {
            i = v;
        }   
    
        unittest {
            A a = new A(1);
            assert(a.i == 1); 
            assert(a.i != 0); 
        }   
    }
    
    int add(int a, int b)
    {
        return a - b;
        // 这里没有unittest
    }
    
    void main()
    {
        // 这里没有unittest
    }
    
    unittest {
        assert(add(1, 2) == 3); 
    }
    

    先正常编译,没有语法错误:
    # dmd -run unittest.d
    

    再编译单元测试代码
    # dmd -unittest -run unittest.d 
    Error: AssertError Failure unittest(30)
    

    30行有错误?add函数写错了:(

    -cov
    看看覆盖率分析选项:
    //cov.d
    import std.stdio;
    
    void main()
    {
        for (int i; i < 2; i++)
        {
            if (i < 5)
                writefln("i < 5");
            else
                writefln("i >= 5");
        }
    }
    

    编译并运行:
    # dmd -cov -run cov.d
    i < 5
    i < 5
    

    得到覆盖率分析文件:cov.lst:(注意:编译完并不会有这个文件;运行程序后才会生成)
           |//cov.d
           |import std.stdio;
           |
           |void main()
           |{
          6|    for (int i; i < 2; i++)
           |    {
          2|        if (i < 5)
          2|            writefln("i < 5");
           |        else
    0000000|            writefln("i >= 5");
           |    }
           |}
    cov.d is 75% covered
    

    第6行运行了6次--自己算算是不是;
    第11行运行了0次--搜索000000字符串,就能轻松找到没有覆盖到地方;
    更多详情参考:http://digitalmars.com/d/code_coverage.html

    -release
    用法很简单,不举例了

    编译开关之二
    -D
        生成文档
    
    -Dddocdir
        把文档生成到docdir目录;注意是 -Dd
    	
    -Dffilename
    	指定文档的文件名;
    

    编译命令很简单:
    dmd -D debug.d
    

    关于文档的更多信息参考:http://sofire.iteye.com/blog/111881

    编译开关之三
    -H
        生成.di接口文件
    
    -Hddir
        把接口文件生成到dir目录;注意是 -Hd
    
    -Hffilename
        指定接口文件名;注意是 -Hf
    

    关于接口,看一段翻译:
    当处理源文件中的import声明时,编译器会搜索import对应的源文件,从中提取出需要的信息。
    编译器同时也会搜索D接口文件,D接口文件中只包含模块中需要导入的内容,而不是整个模块。
    使用D接口文件的好处是:

      D接口文件更小,和D源文件相比处理起来更快。
      可以隐藏源代码。比如以接口文件和object库的方式提供源程序,而不是提供全部源代码。

    D接口文件可以在编译D源文件时用-H开关创建,D接口文件的后缀是.di。
    当编译器分解import声明时,搜索寻找.di形式的D接口文件,再寻找D源文件。
    D接口文件有点和C++头文件相似,但这不是必需的,它不属于D语言,只是编译器的一个功能,只是用来优化程序的构建。

    dmd -H生成的接口文件包括了源代码;只是去掉了注释,断言等信息;具体怎么回事,待弄明白了再来改这里(TODO)



    编译开关之四
    -c
        只编译,不链接;简单点说就是只生成.o文件,不生成可执行文件
    
    -Ipath
    	指定import路径;多个路径之间用分号(;)分割;允许有多个-I,并按照-I指定的路径顺序进行搜索
    
    -Jpath
    	指定D源程序中import表达式的搜索路径;多个路径之间用分号(;)分割;
    	允许有多个-J,并按照-J指定的路径顺序进行搜索
    
    -Llinkerflag
       	 把linkerflag传递给连接程序(linker),比如: -L-L/usr/lib
    
    -o-
        不生成.o文件,一般和-H、-D一起使用
    
    -offilename
        指定输出文件名;可以是可执行程序,也可以是其他文件;注意是:-of
    
    -odobjdir
        把.o文件生成到objdir目录;默认是生成到当前目录;注意是:-od
    
    -op
    	默认生成的object文件(.o)会在当前目录;添加-op参数则会生成到源文件所在目录
    


    -c
    	dmd debug.d     # 生成debug可执行文件
    	dmd -c debug.d  # 生成debug.o文件
    

    dmd编译器默认是编译成.o文件后,再和其他库连接成可执行文件;
    某些情况下不需要编译成可执行文件,比如没有main函数--也编译不成
    这时就可以只编译成.o文件

    bud等程序build工具,可以自动判断文件是否有main函数,并生成相应的文件;
    但dmd编译器不是这样的;所以,熟悉了dmd编译器后,可以只使用bud等工具来编译程序

    -I / -J
    import std.stdio;				// -I
    void main()
    {
        auto b = import("x.d");		// -J
    }
    


    自己体会它们的用途吧:)
    注意:如果在搜索路径下有同名文件的话,可能出现奇怪的问题;避免出现这种情况,或者改变参数的顺序

    -Llinkerflag
    一般是-L-L 和 -L-l参数,指定lib路径和库文件
    比如:-L-L/usr/local/lib  -L-lsqlite3
    对linker不熟悉,回头在详细写这块

    -o-
    如果只想生成文档,连.o文件都不想要;则-o- 就是你想要的了

    -offilename
    默认情况下,是根据源文件名来确定后续文件的文件名;
    比如foo.d 会生成 foo.ofoo 文件;
    通过-of参数可以改变输出文件的名字:
    dmd -ofbar.exe foo.d

    它不会自动添加后缀:
    dmd -c -ofbar foo.d
    生成文件是 bar,而不是bar.o,这可能不是你想要的

    -odobjdir -op
    .o文件默认生成在当前目录下;
    -od 是指定生成目录,-od.和默认相同
    -op 是把.o生成到源文件所在目录
    具体生成到什么目录下,就看自己的爱好了;
    喜欢干净,就用-od吧;对了,-run参数能生成更干净的代码;)

    编译开关之五
    -O
        优化生成的代码,使程序运行得更快
    
    -g
        添加调试信息
    
    -gc
        添加C风格的调试信息(为旧的gdb)
    
    -inline
        用内联函数的方式进行优化;相当于C的inline
    
    -fPIC
        生成位置无关代码
    
    -d
        允许废弃的特征
    
    -profile
        profile the runtime performance of the generated code 
    	参见:http://www.digitalmars.com/ctg/trace.html
    


    这几个参数,要么很简单,要么不懂含义;也懒得去研究具体的意思了。
    其中的-g参数涉及到使用调试器;我喜欢用writefln调试;唉,回头再研究吧。


    编译开关之六
    --help
        打印帮助
    
    -quiet
       	安静模式,不输出无关紧要的信息
    
    -v
        显示编译细节
    
    -w
    	显示编译警告
    


    编译开关之七
    -run srcfile args...
        编译,链接,然后运行程序srcfile;args...(到命令行结束)都是程序的参数;
    	它不会保留.o和可执行程序(No .o or executable file is left behind)
    


    文章开始就有怎么使用的例子
    需要注意的是参数的顺序,因为很多写法都是错误的,正确的是:
    dmd 相关文件 编译开关 -run 含main的源文件 程序参数1 程序参数2
    

    各种参数放在-run前面,然后是含有main的源程序,再后面的内容会全部传递给运行程序,作为参数

    链接Linking
    在dmd编译成功后,它会再调用连接程序;用-c参数可以不进行连接
    连接的实际处理程序其实是gcc;这样能保证和gcc编译的模块兼容


    环境变量
    CC
    默认是用gcc进行连接,可以通过设置CC环境变量,使用其他连接器

    DFLAGS
    The value of DFLAGS is treated as if it were appended to the command line to dmd.
    没有弄明白它是怎么回事;(

    dmd.conf初始化文件
    dmd会按照下面的目录顺序查找
    1. 当前工作目录
    2. 环境变量HOME指定的目录
    3. dmd命令所在目录,即bin目录
    4. /etc目录


    dmd.conf的内容看起来像这样:
    ; dmd.conf 是dmd的配置文件
    ; 分号是注释符号
    ; %%包含的名字会用相应的环境变量替换
    ; %@P%会被本文件的路径替换,即dmd.conf文件所在路径
    [Environment]
    DFLAGS="-I%@P%/../src/phobos"
    DDOCFILE=candydoc/proj.ddoc
    


    格式是 NAME=value;NAME即使是小写,也会被处理成大写;
    里面的DFLAGS的值会覆盖环境变量指定的值

    和Windows版本的区别
    • 字符串文章量是只读的;对它写会导致段错误
    • 配置文件是dmd.conf,而不是sc.ini
    • Windows有一个@cmdfile开关
    • Windows有一个-nofloat开关
    • 环境变量上有些不一样


    总结:
    和gcc比起来,参数少多了 ;)
    虽然可以用bud进行编译,但理解dmd还是必要的。
    反正也不复杂,花点时间学习一下还算值得。

    评论 共 0 条 请登录后发表评论

    发表评论

    您还没有登录,请您登录后再发表评论

    文章信息

    • qiezi在2007-08-15创建
    • qiezi在2011-05-26更新
    Global site tag (gtag.js) - Google Analytics