2007-08-15

Dmd编译器学习笔记

关键字: D语言 dmd 编译器 complier
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的安装
  1. 下载dmd程序:http://ftp.digitalmars.com/dmd.zip,解压到~/dmd目录
  2. 复制dmd.conf文件到/etc目录
  3. cp ~/dmd/bin/dmd.conf /etc
    
  4. 给下面的文件添加执行权限
  5. chmod u+x ~/dmd/bin/{dmd,dumpobj,obj2asm,rdmd}
    
  6. 把~/dmd/bin添加到PATH环境变量;或者把它们复制到/usr/local/bin目录下(不要只复制可执行程序)
  7. 复制库文件到/usr/lib目录
  8. cp ~/dmd/lib/libphobos.a /usr/lib
    



以上安装过程比较简单,只有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.javaeye.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还是必要的。
反正也不复杂,花点时间学习一下还算值得。
  • d_compiler.zip (21.7 KB)
  • 描述: 包括:源文件和D语言的英文帮助
  • 下载次数: 25
评论
zhangtianfeng 2008-01-18
我看了 一些D语言的一些介绍,对于的产生还是有的疑惑.
qiezi 2007-08-17
.so文件是用-L来指定的,比如gcc的-levent是用来链接libevent.so,DMD只需要-L-levent就可以了。
发表评论

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

sofire
搜索本博客
最近加入圈子
存档
最新评论
评论排行榜