back
使用 Xmake 作为 CS149 作业的 bulild system
前言
CS149 是一个教并行编程的课程,课程作业使用了 makefile 作为 build system。
本文通过改造 build system 来讲述一些xmake
的使用方法。
分析
这个课程主要有四个作业,那么是一个多target
工程,考虑用多级配置。
作业里用到了很多工具,xmake
都支持这些工具链。不过有些作业不跨平台(只能在 Linux 下运行),所以尽可能地将作业移植到 windows 上。
改造
完整配置在GitHub上,这里选讲几个部分。
根目录配置
- 最小
xmake
版本。
set_xmakever("2.7.5")
- 跨平台首选
clang
作为 C++ 编译器,在 windows 下使用clang-cl
接受 msvc 风格的参数。 - 在开发环境下,链接动态库可以减少二进制体积,加速链接。
- 添加宏定义处理一些 windows 的屎。
if is_plat("windows") then
-- set_toolchains("clang-cl")
set_runtimes("MD")
add_defines("_CRT_SECURE_NO_WARNINGS", "NOMINMAX")
else
-- set_toolchains("clang")
end
源码目录配置
- 考虑到 windows 没有
getopt.h
,这里在 github 上随便找了一个库代替,并开放头文件权限给依赖此库的target
使用。
if is_plat("windows") then
target("getopt")
set_kind("static")
add_includedirs("getopt-for-windows", {public = true})
add_files("getopt-for-windows/getopt.c")
target_end()
end
作业1
作业有多个target
,其实可以考虑每个给每个target
单独一个配置,不过这里选择把多个target
都放在同一个xmake.lua
中。
- 根据原始 makefile 参数,处理一些跨平台配置。
- 考虑到作业会生成 ppm 格式的图片,所以改变该
target
的运行目录。
target("mandelbrot")
set_kind("binary")
add_files("prog1_mandelbrot_threads/*.cpp")
set_optimize("fastest")
add_deps("common")
if is_plat("windows") then
add_deps("getopt")
elseif is_plat("linux") then
add_syslinks("m", "pthread")
end
set_rundir("prog1_mandelbrot_threads")
target_end()
- 这里用到了
ispc
进行编译,使用xmake
的内置规则进行编译。 - 编译 ispc 文件会生成一个对象文件和头文件,这里通过
header_extension
配置控制生成头文件的后缀,然后就可以在代码中引用生成的头文件xxx_ispc.h
(xxx.ispc)。 - 由于没有内置的 ispc api,这里使用
set_values
传递编译参数。
target("sqrt")
set_kind("binary")
add_rules("utils.ispc", {header_extension = "_ispc.h"})
add_files("prog4_sqrt/*.ispc")
add_files("prog4_sqrt/*.cpp")
add_cxxflags("-march=native")
set_values("ispc.flags", "--target=avx2-i32x8", "--arch=x86-64")
set_optimize("fastest")
add_deps("common")
if is_plat("linux") then
add_syslinks("m", "pthread")
end
target_end()
作业2
本作业两个target
的配置相同,可以用一个循环来生成。(常用于生成相同配置的单元测试)
for _, part in ipairs({"part_a", "part_b"}) do
target(part)
set_kind("binary")
add_includedirs(part)
add_includedirs("../common", "tests")
add_files(part .. "/*.cpp")
add_files("tests/main.cpp")
if is_plat("windows") then
add_deps("getopt")
elseif is_plat("linux") then
add_syslinks("m", "pthread")
end
target_end()
end
- 作业内置 test,但官方没有提供完成所有测试的脚本(其实直接改源码也可以),不过这里考虑用 xmake 的脚本域来做这件事。
- 设置一个空目标类型,覆盖内置的on_run,然后使用 xmake 一些内置的 api 运行测试。
target("part_a_test")
set_kind("phony")
add_deps("part_a")
on_run(function ()
local test_names =
{
"simple_test_sync",
"ping_pong_equal",
"ping_pong_unequal",
"super_light",
"super_super_light",
"recursive_fibonacci",
"math_operations_in_tight_for_loop",
"math_operations_in_tight_for_loop_fewer_tasks",
"math_operations_in_tight_for_loop_fan_in",
"math_operations_in_tight_for_loop_reduction_tree",
"spin_between_run_calls",
"mandelbrot_chunked",
}
for _, value in ipairs(test_names)
do
-- cprint("Testing " .. "${bright green}%s", value)
-- os.runv(name, {value})
os.execv("xmake", {"run", "part_a", value})
end
cprint("${bright red}Pass!")
end)
target_end()
作业3
xmake 会自动探测 cuda 的目录,如果不在默认的安装目录,设置一下。
xmake f --cuda=<PATH>
- xmake 对
cuda
支持还算完善,直接add_files
就能直接编译。 - cuda runtime 也有动态库,不过 xmake 默认链接静态库,这里需要手动添加链接动态库的名字。
add_cugencodes("compute_35")
add_links("cudart")
target("render")
set_kind("binary")
add_files("render/*.cpp")
add_files("render/*.cu")
add_packages("freeglut")
set_rundir("render")
target_end()
作业4
该作业只能运行在 Linux 平台,因为官方只提供了 Linux 平台的二进制进行链接。
- 这里写了一个函数禁用
target
,然后在on_load时禁用,可以使用其他写法禁用target
。 - 官方提供了一个静态库,但静态库名字前缀不是
lib
开头,链接器不接受,所以不能直接用add_links
function only_linux(target)
if target:is_plat("linux") then
target:set("enabled", true)
else
target:set("enabled", false)
end
end
target("pr")
set_kind("binary")
add_files("pagerank/main.cpp", "pagerank/page_rank.cpp")
add_linkdirs("pagerank")
add_ldflags("-fopenmp")
add_ldflags("-l:ref_pr.a", {force = true})
add_deps("assignment4_common")
on_load(only_linux)
target_end()
- 对象文件也可也直接add_files
target("bfs")
set_kind("binary")
add_files("bfs/main.cpp", "bfs/bfs.cpp")
add_files("bfs/ref_bfs.o")
add_ldflags("-fopenmp")
add_deps("assignment4_common")
on_load(only_linux)
target_end()