当前位置:首页 > 网站旧栏目 > 学习园地 > 设计软件教程 > Dmd编译器学习笔记

Dmd编译器学习笔记
2010-01-13 22:54:09  作者:  来源:
Dmd编译器学习笔记

英文原文在这里:
http://digitalmars.com/d/dcompiler.html
在这里有一篇翻译文章:
http://sofire.javaeye.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目录
    Java代码 复制代码
    1. cp ~/dmd/bin/dmd.conf /etc  
  • 给下面的文件添加执行权限
    Java代码 复制代码
    1. chmod u+x ~/dmd/bin/{dmd,dumpobj,obj2asm,rdmd}  
  • 把~/dmd/bin添加到PATH环境变量;或者把它们复制到/usr/local/bin目录下(不要只复制可执行程序)
  • 复制库文件到/usr/lib目录
    Java代码 复制代码
    1. cp ~/dmd/lib/libphobos.a /usr/lib  

    [/list]

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

    编译参数和开关
    命令的格式:
    Java代码 复制代码
    1. dmd   files...  -switches...   

    [Windows]支持以下类型的文件:
    Java代码 复制代码
    1. Extension      File Type     
    2. none           D source files     
    3. .d             D source files     
    4. .di            D interface files     
    5. .obj           Object files to link in     
    6. .lib           Object code libraries to search     
    7. .exe           Name output executable file     
    8. .def           module definition file     
    9. .res           resource file    

    [Linux]支持以下类型的文件:
    Java代码 复制代码
    1. Extension      File Type   
    2. none           D source files   
    3. .d             D source files   
    4. .di            D interface files   
    5. .o             Object files to link in   
    6. .a             Library files to link in  

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

    编译开关之一
    Java代码 复制代码
    1. -debug   
    2.     编译调试代码   
    3.   
    4. -debug=level   
    5.     编译调试代码:code <= level   
    6.   
    7. -debug=ident   
    8.     编译调试代码:标识符为ident   
    9.   
    10. -version=level   
    11.     生成版本代码:>=level   
    12.   
    13. -version=ident   
    14.     生成版本代码:==ident   
    15.        
    16. -unittest   
    17.     编译单元测试代码(还有断言)   
    18.   
    19. -cov   
    20.     添加覆盖率分析指令;运行程序后,会生成.lst文件   
    21.   
    22. -release   
    23.     生成发行版本;会去掉契约和断言等信息  


    -debug / -version
    debug、version的使用方法很相似
    Java代码 复制代码
    1. //debug.d   
    2. import std.stdio;   
    3.   
    4. void main()   
    5. {   
    6.     debug    { writefln("debug"); }   
    7.     debug(1) { writefln("debug(1)"); }   
    8.     debug(2) { writefln("debug(2)"); }   
    9.   
    10.     debug(ERROR) { writefln("debug(ERROR)"); }   
    11.     debug(WARN)  { writefln("debug(WARN)"); }   
    12.   
    13.     version(HOME)      { writefln("version(HOME)"); }   
    14.     version(BUSINESS)  { writefln("version(BUSINESS)"); }   
    15.   
    16.     version(WINDOWS) {} else { writefln("version(!WINDOWS)"); }   
    17. }  

    编译并运行之:
    Java代码 复制代码
    1. # dmd -debug -run debug.d   
    2. debug   
    3. debug(1)   
    4. version(!WINDOWS)   
    5.   
    6. # dmd -debug=1 -run debug.d   
    7. debug   
    8. debug(1)   
    9. version(!WINDOWS)   
    10.   
    11. # dmd -debug=2 -run debug.d   
    12. debug   
    13. debug(1)   
    14. debug(2)   
    15. version(!WINDOWS)   
    16.   
    17. # dmd -debug=ERROR -run debug.d   
    18. debug(ERROR)   
    19. version(!WINDOWS)   
    20.   
    21. # dmd -debug=WARN -run debug.d   
    22. debug(WARN)   
    23. version(!WINDOWS)   
    24.   
    25. # dmd -version=HOME -run debug.d   
    26. version(HOME)   
    27. version(!WINDOWS)   
    28.   
    29. # dmd -version=BUSINESS -version=WINDOWS -run debug.d   
    30. version(BUSINESS)  


    -unittest
    Java代码 复制代码
    1. //unittest.d   
    2. import std.stdio;   
    3.   
    4. class A   
    5. {   
    6.     int i;   
    7.     this(int v) {   
    8.         i = v;   
    9.     }      
    10.   
    11.     unittest {   
    12.         A a = new A(1);   
    13.         assert(a.i == 1);    
    14.         assert(a.i != 0);    
    15.     }      
    16. }   
    17.   
    18. int add(int a, int b)   
    19. {   
    20.     return a - b;   
    21.     // 这里没有unittest   
    22. }   
    23.   
    24. void main()   
    25. {   
    26.     // 这里没有unittest   
    27. }   
    28.   
    29. unittest {   
    30.     assert(add(12) == 3);    
    31. }  

    先正常编译,没有语法错误:
    Java代码 复制代码
    1. # dmd -run unittest.d  

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

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

    -cov
    看看覆盖率分析选项:
    Java代码 复制代码
    1. //cov.d   
    2. import std.stdio;   
    3.   
    4. void main()   
    5. {   
    6.     for (int i; i < 2; i++)   
    7.     {   
    8.         if (i < 5)   
    9.             writefln("i < 5");   
    10.         else  
    11.             writefln("i >= 5");   
    12.     }   
    13. }  

    编译并运行:
    Java代码 复制代码
    1. # dmd -cov -run cov.d   
    2. i < 5  
    3. i < 5  

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

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

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

    编译开关之二
    Java代码 复制代码
    1. -D   
    2.     生成文档   
    3.   
    4. -Dddocdir   
    5.     把文档生成到docdir目录;注意是 -Dd   
    6.        
    7. -Dffilename   
    8.     指定文档的文件名;  

    编译命令很简单:
    Java代码 复制代码
    1. dmd -D debug.d  

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

    编译开关之三
    Java代码 复制代码
    1. -H   
    2.     生成.di接口文件   
    3.   
    4. -Hddir   
    5.     把接口文件生成到dir目录;注意是 -Hd   
    6.   
    7. -Hffilename   
    8.     指定接口文件名;注意是 -Hf  

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

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

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

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



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


    -c
    Java代码 复制代码
    1. dmd debug.d     # 生成debug可执行文件   
    2. dmd -c debug.d  # 生成debug.o文件  

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

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

    -I / -J
    Java代码 复制代码
    1. import std.stdio;               // -I   
    2. void main()   
    3. {   
    4.     auto b = import("x.d");     // -J   
    5. }  


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

    -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参数能生成更干净的代码;)

    编译开关之五
    Java代码 复制代码
    1. -O   
    2.     优化生成的代码,使程序运行得更快   
    3.   
    4. -g   
    5.     添加调试信息   
    6.   
    7. -gc   
    8.     添加C风格的调试信息(为旧的gdb)   
    9.   
    10. -inline   
    11.     用内联函数的方式进行优化;相当于C的inline   
    12.   
    13. -fPIC   
    14.     生成位置无关代码   
    15.   
    16. -d   
    17.     允许废弃的特征   
    18.   
    19. -profile   
    20.     profile the runtime performance of the generated code    
    21.     参见:http://www.digitalmars.com/ctg/trace.html  


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


    编译开关之六
    Java代码 复制代码
    1. --help   
    2.     打印帮助   
    3.   
    4. -quiet   
    5.     安静模式,不输出无关紧要的信息   
    6.   
    7. -v   
    8.     显示编译细节   
    9.   
    10. -w   
    11.     显示编译警告  


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


    文章开始就有怎么使用的例子
    需要注意的是参数的顺序,因为很多写法都是错误的,正确的是:
    Java代码 复制代码
    1. 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的内容看起来像这样:
    Java代码 复制代码
    1. ; dmd.conf 是dmd的配置文件   
    2. ; 分号是注释符号   
    3. ; %%包含的名字会用相应的环境变量替换   
    4. ; %@P%会被本文件的路径替换,即dmd.conf文件所在路径   
    5. [Environment]   
    6. DFLAGS="-I%@P%/../src/phobos"  
    7. DDOCFILE=candydoc/proj.ddoc  


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

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


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

安徽新华电脑学校专业职业规划师为你提供更多帮助【在线咨询