仿真rake的elisp实现
类似make,elake中的任务也分为两种,phony任务与file任务. 其中file任务对应操作系统中的一个文件路径.
一个任务的依赖任务是否执行由以下表格表示:
| 目标任务类型 | 依赖任务类型 | 依赖任务执行条件 |
|---|---|---|
| phony任务 | phony任务 | 依赖任务未执行 |
| file任务 | phony任务 | 依赖任务未执行 |
| phony任务 | file任务 | 依赖任务对应的文件不存在 |
| file任务 | file任务 | 依赖任务对应的文件要比目标任务对应的文件更新 |
定义任务的方法是使用宏 (elake-task 目的任务 (依赖任务列表) 任务说明字符串 任务定义内容).
例如我们这样定义一个做饭的过程:
(elake-task :purchaseVegetables nil
"任务1 -- 买菜"
(message "到沃尔玛去买菜。"))
(elake-task :cook (:purchaseVegetables)
"任务2 -- 做饭"
(message "做一顿香喷喷的饭菜。"))上面代码定义了两个任务
第一个任务的名称为”:purchaseVegetables”,它以”:”开头,因此是一个phony任务.
- :purchaseVegetables任务的依赖任务列表为nil,表示它不依赖任何前置任务
- :purchaseVegetables任务的说明是”任务1 – 买菜”
- :purchaseVegetables任务的内容是
(message "到沃尔玛去买菜。")
第二个任务的名称为”:cook”,它是一个phony任务. 它依赖于:purchaseVegetables任务.
因此,在执行:cook任务前会先执行:purchaseVegetables
$ elake :cook 到沃尔玛去买菜。 做一顿香喷喷的饭菜。
在任务定义中,可以使用$<指代目标任务,使用$@指代依赖任务列表. 例如:
(elake-task :task1 (/etc/passwd)
"测试自动变量"
(message "%s依赖于%s" $< $@))执行任务:task1的结果为:
$ elake :task1 :task1依赖于(/etc/passwd)
elake会将地一个定义的任务作为默认的构建任务. 当没有为elake指定构建哪个任务时,elake会构建默认的构建任务.
例如,上面的例子中,我们地一个定义的任务是:purchaseVegetables任务,因此直接调用elake的结果为:
$ elake 到沃尔玛去买菜。
elake支持命名空间的概念. 使用命名空间的格式为 (elake-namespace 命名空间名称 其他elake命令...)
在命名空间内定义的phony任务,其完整的名称为”:命名空间名称:任务名称”,命名空间对file任务无效 例如:
(elake-namespace home
(elake-task :write-blog (:turn-on-computer) ;注意,同一个命名空间内的依赖任务无需加命名空间前缀
"写博客"
(message "在家写博客"))
(elake-task :turn-on-computer ()
"打开电脑"
(message "打开家里的电脑")))这里在home命名空间内定义了两个任务:”:home:write-bog和:home:turn-on-computer”.
$ elake :home:write-blog 打开家里的电脑 在家写博客 $ elake :write-blog 未定义的任务::write-blog
目前支持命名空间的函数还有`elake-execute-task’和`elake-remove-task’
给elake传递形如”环境变量:=值”的参数,可以设定环境变量的值
(elake-task :set-env nil
"设置环境变量"
(message "UNKNOW-ENV:%s" (getenv "UNKNOW-ENV")))若不传递环境变量,则有:
$ elake :set-env UNKNOW-ENV:nil
若传递环境变量,则:
$ elake :set-env UNKNOW-ENV:=whatever UNKNOW-ENV:whatever
可以在定义任务时,使用未定义的变量. 只需要通过形如”变量=值”的形式传递变量值就好. 例如
(elake-task :say-hello-to nil
"给任务传递参数"
(message "hello to %s" who))若不传递参数值,则会执行出错
$ elake :say-hello-to Symbol's value as variable is void: who
若传递参数值,则
$ elake who=darksun :say-hello-to hello to darksun
使用 (elake-execute-task task) 可以在任务内执行其他任务
(elake-task :laundry nil
"洗衣服"
(message "把所有衣服扔进洗衣机。"))
(elake-task :today nil
"今天的任务"
(elake-execute-task :home:write-blog)
(elake-execute-task :laundry))则调用:today任务,会以此执行:home:write-blog和:laundry任务
$ elake :today 打开家里的电脑 在家写博客 把所有衣服扔进洗衣机。
使用 (elake-remove-task task) 可以删除其他任务.例如
(elake-task :task-to-be-removed nil
"待删除的任务"
(message "该任务会被:remove-task删除掉,删除掉后无法执行"))
(elake-task :remove-task (:task-to-be-removed)
"删除任务:task-to-be-removed"
(elake-remove-task :task-to-be-removed)
(message "%s被删除" $@))
(elake-task :execute-removed-task (:remove-task)
"删除任务测试"
(elake-execute-task :task-to-be-removed))
在执行:remove-task前,可以执行:task-to-be-removed
$ elake :remove-task 该任务会被:remove-task删除掉,删除掉后无法执行 (:task-to-be-removed)被删除
但执行:remove-task后,无法再执行:task-to-be-removed了
$ elake :execute-removed-task 该任务会被:remove-task删除掉,删除掉后无法执行 (:task-to-be-removed)被删除 未定义的任务::task-to-be-removed
使用 (elake-rule 目标任务的匹配正则表达式 依赖任务的替代表达式列表 任务内容) 来定义规则,当执行未定义的任务时,elake会尝试使用规则生成该任务.
这里匹配正则表达式必须为字符串,替代表达式列表为字符串或字符串列表.
关于匹配正则表达式和替代表达式的说明请参见`replace-regexp’函数中的`REGEXP’和`TO-STRING’说明.
例如:
(elake-rule ":make-\\(.+\\)" ("\\1")
"测试rule"
(message (shell-command-to-string (format "ls -l %s" (car $@)))))
(elake-rule "[^:].*" nil
"测试rule"
(message "touch %s" $<)
(message (shell-command-to-string (format "touch %s" $<))))可以使用:make-xxx任务来显示xxx文件的内容,且若xxx文件不存在则创建该文件
$ ls -l rule rule: No such file or directory $ elake :make-rule touch rule -rw-rw-r-- 1 lujun9972 lujun9972 0 7月 19 11:53 rule $ elake :make-rule -rw-rw-r-- 1 lujun9972 lujun9972 0 7月 19 11:53 rule
依赖任务列表中支持使用S-Form自动生产依赖任务,若依赖任务为list,则该list会被作为S-Form来执行,该S-Form的执行结果应该为一个包含一个多个多个依赖任务的list.
例如:
(elake-task :blog (index.html (let (files)
(dotimes (num 3 files)
(push (format "file-%s.html" num) files))))
"测试使用S-Form自动生成依赖任务"
(message "%s的依赖任务为%s" $< $@))表示:blog的依赖任务为index.html,file-2.html,file-1.html,file-0.html
因此:
~/elake $ elake :blog touch index.html touch file-2.html touch file-1.html touch file-0.html :blog的依赖任务为(index.html file-2.html file-1.html file-0.html)
- -f 配置文件路径
- 使用指定的文件作为加载的配置文件. 默认使用`elakefile’或`elakefile.el’或`elakefile.elc’
- –task 任务…
- 显示任务说明,若省略后面的任务,则显示所有任务说明
- -p 任务…
- 显示任务的依赖任务,若省略后面的任务,则显示所有的任务依赖
- -h
- 显示帮助信息
- [X] 增加file类型的任务
- [X] 增加命名空间的支持
- [X] 仿照make添加一些自动变量
- [X] 仿照make将第一个定义的任务作为默认的构建任务
- [ ] 如何支持emacs本身已经占有的那些option呢?
- [ ] 如何区分传递符号和字符串給emacs –script呢?
- [ ] 支持定义任务和定义依赖相分离
- [ ] 支持分多次添加依赖关系,这样就能用程序自动添加依赖关系
- [X] 支持模式规则来自动生成某类文件
- [X] 支持使用-f指定依赖文件,默认为elakefile或elakefile.el或elakefile.elc
- [X] 支持带参数的任务
- [X] 支持命令行设置环境参数
- [X] 提供删除已定义任务的机制
- [X] 依赖任务列表中支持使用S-Form自动生成依赖任务