V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
waruqi
V2EX  ›  程序员

xmake 入门,构建项目原来可以如此简单

  •  2
     
  •   waruqi ·
    waruqi · 2018-03-29 08:30:50 +08:00 · 3719 次点击
    这是一个创建于 2435 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    在开发xmake之前,我一直在使用 gnumake/makefile 来维护个人 C/C++项目,一开始还好,然而等项目越来越庞大后,维护起来就非常吃力了,后续也用过一阵子 automake 系列工具,并不是很好用。

    由于 C/C++程序的构建过程比较繁琐,如果不借助 IDE 工具,很难快速构建一个新的 C/C++程序,想要跨平台构建就更加麻烦了。

    虽然 IDE 很好用,也很强大,但是还是有很多不足的地方,例如:

    • 跨平台开发支持不完善
    • 自身环境不一定跨平台
    • 过于臃肿
    • 不利于服务端自动化部署构建
    • 不够灵活,定制化配置构建过程有局限性

    当然如果你熟悉 makefile 的话,也可以手敲 makefile,不过不同平台用的 make 也不相同,比如: gnumake, nmake 等,导致 makefile 语法存在差异性,无法做到一致性编译,而且对开发者有一定的使用门槛。

    在 win 上使用 gnumake 还得装 cygwin,mingw-msys 等环境,也非常麻烦,折腾完环境就得半天时间。

    目前已经有了很多现代化的构建工具,方便开发者构建和维护 C/C++项目,例如:cmake, scons, premake, bazel, gn, gyp 等等。

    其中很多只能生成对应的 IDE 工程,然后再通过对应 IDE 来维护和构建,这种只是解决了 C/C++项目的一致性维护问题,但是构建方式不一致,因此还是没解决之前列举的大部分不足点,也无法直接快速构建。

    而 cmake, scons 虽然很强大,但是 cmake 语法怪异不直观,本人实在是不习惯,scons 使用还需要依赖 python,py2/py3 的问题折腾起来也比较蛋疼。

    鉴于此,我采用了 lua 来描述工程,利用 lua 的轻量,简洁,灵活,跨平台等特性,来解决上述遇到的各种问题,使用 xmake 将会带来不一样的构建体验:

    • 轻量,跨平台,无依赖,无需额外安装 python 等第三方环境,直接内置 lua 运行时,一个安装包(或者命令)直接搞定
    • 工程描述直观简洁,更符合用户正常的思维习惯
    • 支持直接构建,强大的命令行工具,终端用户的福音,装逼用户必备
    • vscode, idea, clion, sublime, vim 等编辑器插件支持
    • 智能检测支持,简化用户编译配置过程
    • 插件支持,灵活的用户可扩展性
    • vcproj 等 IDE 项目文件生成也支持的哦
    • 更多隐藏特性等你来体验

    xmake-compilation

    快速上手

    不会写 makefile ?没关系,直接在源码目录运行以下命令即可直接编译:

    xmake
    

    xmake 会自动扫描在当前目录下的源码结构,生成一个xmake.lua工程描述文件,然后尝试直接编译。

    想要直接运行编译后的可执行程序,简单,直接敲:

    $ xmake run
    

    更多相关信息,请参考文章: xmake 新增智能代码扫描编译模式,无需手写任何 make 文件

    快速入门

    如果想要更进一步描述工程,调整源码结构,添加一些编译选项什么的,还是需要维护一个名叫 xmake.lua 的工程描述文件,类似 makefile, cmakelist.txt ,但是其语法和 api 经过不断地改进简化,已经相当易用。

    最简单的描述例子只需要三行:

    target("test")
        set_kind("binary")
        add_files("src/*.c")
    

    就可以构建一个可执行程序,编译所有在 src 目录下的 c 源文件。

    然后直接执行 xmake 即可编译。

    add_files()支持通配符文件模式匹配,并且支持.c, .cpp, .go, .d, .m, .mm, .S, .swift, .rc, .rs等各种 native 语言的代码文件,大部分都能支持混编。

    我们甚至可以添加.a 和.o, .obj 文件到add_files(),例如:

    target("test")
        set_kind("static")
        add_files("src/*.c")
        add_files("lib/libxxx.a", "obj/bbb.o")
    

    上述描述会编译生成一个 libtest.a 库,在编译归档的时候,会自动将 libxxx.a 库反解出来,合并到 libtest.a 中去,并且同时将 bbb.o 也加进去。

    xmake 提供的add_files是非常强大的,我们还可以再添加一批文件的同时,指定排除某些文件,例如:

    add_files("src/**.cpp|test.cpp|arm/*.cpp")
    

    上述描述,在递归添加源文件的同时,排除掉了 test.cpp 以及 arm 目录下的源文件。

    更多add_files用法,请参考文档:add_files 接口使用文档

    使用演示

    命令行下的使用过程,大家可以通过一个视频直观的体验下:

    创建工程

    更加省事的方式就是通过上节所说傻瓜式操作方式,自动生成一个 xmake.lua ,然后在这基础下修修改改就行了。

    当然如果没有现成源码,想从新工程创建开始编译,那么可以使用 xmake 提供的工程模板进行创建:

    $ xmake create test
    

    默认创建一个名为 test 的 c 可执行项目,源码结构如下:

    .
    ├── src
    │   └── main.c
    └── xmake.lua
    

    当然你也可以选择语言和模板类型:

    $ xmake create -l c++ -t shared test
    

    上述命令创建了一个 c++动态库项目,就这么简单。

    运行和调试

    编译完的可执行程序,直接敲xmake run就能运行,xmake 会自动找到对应的 target 目标文件,你也可以传递参数给程序。

    如果有多个 target 目标,你可以指定需要运行的 target 名,例如:

    $ xmake run test
    

    想要快速调试程序?加上-d参数即可

    $ xmake run -d test
    

    xmake 默认会去找系统自带的调试器,然后加载运行,windows 上使用 vsjitdebugger,linux 上 gdb,macos 上 lldb,当然你也可以随意切换到其他调试器。

    配合 debug 模式编译,就能做到使用 xmake 进行源码调试。

    可视化配置和构建

    xmake 提倡使用命令行的方式来操作,用习惯后效率非常高,而且在 windows 上,即使没有 cygwin,也可以直接在 cmd 下正常运行。

    当然,并不是所有用户习惯命令行,因此 xmake 也提供了编辑器插件,与各大编辑器进行集成,例如:

    xmake-vscode 插件

    xmake-idea 插件

    xmake-sublime 插件

    xmake-tui 界面

    除了编辑器插件,xmake 甚至自己封装实现了一整套跨平台 tui 字符界面库,然后仿 kconfig/menuconf 的界面风格,实现了一个类似的可视化字符界面菜单配置。

    这个不需要额外的插件,只需要在终端下执行:

    $ xmake f --menu
    

    就可以显示菜单配置界面进行编译配置,配置完即可根据当前配置进行编译,效果如下:

    定制化编译

    想要更加灵活的编译配置?那就得要修改 xmake.lua 啦,不过还是很简单的。

    添加编译选项

    target("test")
        set_kind("binary")
        add_files("src/*.c")
        if is_mode("debug") then
           add_cxflags("-DDEBUG")
        end
    

    上面代码中,add_cxflags接口就是同时配置 C/C++代码的编译选项,并且只在 debug 模式下生效,也就是执行下面命令的时候:

    $ xmake f -m debug
    $ xmake
    

    使用内置选项

    像添加宏定义,设置警告级别,优化级别,头文件搜索目录什么的,完全没必要使用原始的add_cxflags接口,xmake 有提供更加方便的接口,更加智能化的处理来简化配置,也更加通用跨平台,例如:

    add_defines("DEBUG")
    set_optimize("fast")
    set_warnings("all", "error")
    
    target("test")
        set_kind("binary")
        add_files("src/*.c")
    
    target("test2")
        set_kind("binary")
        add_files("src2/*.c")
    

    跟刚才的配置不同的是,此处设置放在了 target 的上面,此处不属于 target 域,是 root 全局设置,会影响下面的所有 target 目标程序的编译设置,这样可以简化配置,避免冗余。

    灵活的脚本控制

    对于高端用户,构建需求复杂多变,xmake 也提供了对应解决方案,各个构建阶段都可以灵活定制:

    target("test")
        set_kind("binary")
        add_files("src/*.c")
    
        after_build(function (target)
            os.exec("file %s", target:targetfile())
        end)
    

    上述代码在编译程序结束后,执行 file 命令查看目标程序相关信息,目前 xmake 可以在 build, clean, run, install, uninstall 等各个阶段的前后插入自定义的脚本,也可以直接内置 action,例如: on_install 会覆盖内置的安装逻辑,提供给用户足够的灵活性。

    方便的多目标依赖

    很多时候,一个项目会有多个 target 目标程序,之间存在依赖关系,例如: 一个可执行程序 hello,依赖一个静态库 libtest.a,我们只需要通过 add_deps 将两个 target 做个关联就行了,libtest.a 的搜索目录,头文件目录设置什么的都不需要关心,xmake 会自动处理:

    target("test")
        set_kind("static")
        add_files("src/test/*.c")
    
    target("hello")
        add_deps("test")  --添加依赖
        set_kind("binary")
        add_files("src/hello/*.c")
    

    预编译头文件支持

    xmake 支持通过预编译头文件去加速 c/c++程序编译,目前支持的编译器有:gcc, clang 和 msvc。

    target("test")
        -- ...
        set_pcxxheader("header.h")
    

    各大编译器对预编译头的处理方式存在很大差异,而 xmake 将其差异性隐藏了起来,提供一致性的描述设置,简化用户在跨平台编译时候的处理, 具体关于编译器对预编译头文件的处理,可参考相关文章:不同编译器对预编译头文件的处理

    自定义编译规则

    xmake 不仅原生内置支持多种语言文件的构建,而且还可以通过自定义构建规则,让用户自己来实现复杂的未知文件构建。

    我们可以通过预先设置规则支持的文件后缀,来扩展其他文件的构建支持:

    -- 定义一个 markdown 文件的构建规则
    rule("markdown")
        set_extensions(".md", ".markdown")
        on_build(function (target, sourcefile)
            os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html"))
        end)
    
    target("test")
        set_kind("binary")
        
        -- 使 test 目标支持 markdown 文件的构建规则
        add_rules("markdown")
    
        -- 添加 markdown 文件的构建
        add_files("src/*.md")
        add_files("src/*.markdown")
    

    我们也可以指定某些零散的其他文件作为 markdown 规则来处理:

    target("test")
        -- ...
        add_files("src/test/*.md.in", {rule = "markdown"})
    

    注:通过add_files("*.md", {rule = "markdown"})方式指定的规则,优先级高于add_rules("markdown")设置的规则。

    IDE 工程文件生成

    xmake 提供了丰富的插件扩展,其中 vcproj, makefile 等工程文件的生成就是作为插件提供,使用起来也非常简单:

    $ xmake project -k vs2017 -m "debug,release"
    

    即可生成带有 debug, release 两种编译模式的 vc 工程,同时支持 x86 和 x64。

    生成的工程目录结构会根据添加的所有源文件的目录结构,自动分析生成直观的文件树,方便 vs 去浏览查看。

    makefile 的生成如下:

    $ xmake project -k makefile
    

    后续会陆续更多其他工程文件,也欢迎大家来贡献哦。

    灵活简单的插件扩展

    上节的 IDE 工程文件生成,在 xmake 中就是作为插件来提供,这样更加方便扩展,也能让用户快速定制自己的插件,只需要定义个 task 插件任务就行了:

    -- 定义一个名叫 hello 的插件任务
    task("hello")
    
        -- 设置类型为插件
        set_category("plugin")
    
        -- 插件运行的入口
        on_run(function ()
            print("hello xmake!")
        end)
    
        -- 设置插件的命令行选项,这里没有任何参数选项,仅仅显示插件描述
        set_menu {
                    -- usage
                    usage = "xmake hello [options]"
    
                    -- description
                ,   description = "Hello xmake!"
    
                    -- options
                ,   options = {}
                } 
    

    上述代码就是一个最为简单的hello xmake!插件,运行$xmake hello就可看到执行输出,set_menu用于配置插件命令行选项,这个不设置就是内部 task,无法在命令行下调用。

    更加详细的插件说明以及内置插件列表可参考文档:插件手册

    查找依赖包

    xmake 参考了 cmake 对于find_*系列接口的设计,实现在项目中动态的查找和添加包依赖。

    target("test")
        set_kind("binary")
        add_files("*.c")
        on_load(function (target)
            import("lib.detect.find_package")
            target:add(find_package("zlib"))
        end)
    

    上述描述代码,通过lib.detect.find_package来查找包,如果找到 zlib 包,则将 links, includedirs 和 linkdirs 等信息添加到 target 中去。

    交互式命令执行(REPL)

    有时候在交互模式下,运行命令更加的方便测试和验证一些模块和 api,也更加的灵活,不需要再去额外写一个脚本文件来加载,不过我一般用来做计算器用用(好吧。。)

    # 不带任何参数执行,就可以进入
    $ xmake lua
    >
    
    # 进行表达式计算
    > 1 + 2
    3
    
    # 赋值和打印变量值
    > a = 1
    > a
    1
    
    # 多行输入和执行
    > for _, v in pairs({1, 2, 3}) do
    >> print(v)
    >> end
    1
    2
    3
    

    我们也能够通过 import 来导入扩展模块:

    > task = import("core.project.task")
    > task.run("hello")
    hello xmake!
    

    编译环境支持

    当前 xmake 的最新版本已经支持很多 sdk 环境的集成编译,例如:

    • [x] Visual Studio 编译环境
    • [x] mingw 编译环境
    • [x] cygwin 编译环境
    • [x] Android NDK 编译环境
    • [x] Xcode 编译环境(支持 iPhoneos/Macosx 构建)
    • [x] 系统 gcc/clang 编译环境
    • [x] 交叉工具链编译环境
    • [x] Cuda 编译环境
    • [ ] Qt 编译环境(正在支持中)
    • [ ] Windows WDK 编译环境(正在支持中)

    FAQ

    xmake 有哪些用途?

    1. 跨平台维护和编译 C/C++项目
    2. CI 上部署自动化构建
    3. 开源代码的快速移植
    4. 临时的测试代码编写和快速运行
    5. 与自己喜欢的编辑器集成,打造属于自己的 C/C++开发环境
    6. 与其他 native 语言的混合编译
    7. 嵌入式开发下的交叉编译
    8. 提升逼格

    对于第三点的用途,我平常用的最多,因为我经常需要移植第三方的开源项目,它们使用的构建工具各不相同,有 automake,cmake 等等,其支持的构建平台力度也都不相同,经常会遇到需要的平台不支持的问题。

    没办法,只好自己敲 makefile 来移植代码,然后适配自己需要支持的那些平台,还有交叉工具链,很蛋疼,自从写了 xmake 后,我现在平常移植代码方便了很多,效率提升非常明显。

    怎样看实时编译警告信息?

    为了避免刷屏,在构建时候,默认是不实时输出警告信息的,如果想要看的话可以加上-w选项启用编译警告输出就行了。

    $ xmake [-w|--warning] 
    

    怎样看详细的编译参数信息?

    请加上 -v 或者 --verbose 选项重新执行 xmake 后,获取更加详细的输出信息

    例如:

    $ xmake [-v|--verbose] 
    

    如果加上 --backtrace 选项也可以获取出错时的 xmake 的调试栈信息

    $ xmake -v --backtrace
    

    xmake-verbose

    快速安装

    最后我们讲下,如何安装 xmake,通常只需要一个脚本命令就能搞定。

    一键安装脚本

    bash <(curl -fsSL https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.sh)
    

    windows 安装包

    对于 windows 用户,提供了安装包来快速安装,可到Github Releases上下载对应版本。

    更加详细的安装过程,见相关文档: 安装说明

    结语

    xmake 还有很多非常有用的特性,例如:编译器特性检测、丰富的模块库、依赖包管理、自定义选项等等,一篇文章讲不完这么多,大家有兴趣的话,可以去官方文档里面看看,还有很多隐藏特性等着你哦。

    18 条回复    2018-03-29 17:48:10 +08:00
    wuhau
        1
    wuhau  
       2018-03-29 08:51:30 +08:00 via iPhone
    Mark 前排广告位
    urmyfaith
        2
    urmyfaith  
       2018-03-29 08:59:20 +08:00   ❤️ 1
    可以啊
    V4Exp
        3
    V4Exp  
       2018-03-29 09:54:09 +08:00   ❤️ 1
    很不错的样子!
    devtiange
        4
    devtiange  
       2018-03-29 10:12:25 +08:00   ❤️ 1
    感觉要火
    lniwn
        5
    lniwn  
       2018-03-29 10:17:59 +08:00 via Android   ❤️ 1
    关注好久了,作者确实厉害
    waruqi
        6
    waruqi  
    OP
       2018-03-29 10:22:57 +08:00
    多谢大家支持,有兴趣的同学可以尝试下,也可加 QQ 群(343118190)互相交流。:)
    ivechan
        7
    ivechan  
       2018-03-29 11:29:52 +08:00
    谢谢楼主, 试用了一下 scons 感觉不错。。。
    以后再尝试下 xmake
    ytlm
        8
    ytlm  
       2018-03-29 11:30:03 +08:00   ❤️ 1
    star
    abmin521
        9
    abmin521  
       2018-03-29 11:49:38 +08:00 via Android
    支持 官方加个证书吧
    waruqi
        10
    waruqi  
    OP
       2018-03-29 11:56:37 +08:00 via Android
    @abmin521 嗯嗯 有时间加个
    LeoQian
        11
    LeoQian  
       2018-03-29 16:57:59 +08:00 via iPhone
    mark
    csl1995
        12
    csl1995  
       2018-03-29 16:58:01 +08:00
    苯垃圾只会改 makefile,已收藏学习,👍
    waruqi
        13
    waruqi  
    OP
       2018-03-29 17:09:07 +08:00
    @csl1995 手敲 makefile,厉害。。
    itfanr
        14
    itfanr  
       2018-03-29 17:13:19 +08:00
    @csl1995 大规模项目,手敲会累死人的
    coderluan
        15
    coderluan  
       2018-03-29 17:17:10 +08:00
    cmake 的怪异语法已经用习惯了,心理支持个。
    omph
        16
    omph  
       2018-03-29 17:21:38 +08:00
    和什么 CI 兼容比较好?
    waruqi
        17
    waruqi  
    OP
       2018-03-29 17:46:26 +08:00
    @coderluan 看个人喜好,自己习惯了就好。
    waruqi
        18
    waruqi  
    OP
       2018-03-29 17:48:10 +08:00
    @omph 基本上只要 ci 上 gcc 什么的不缺,基本上都兼容,我实测过的有 travis-ci, appveyor (win)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3445 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 11:22 · PVG 19:22 · LAX 03:22 · JFK 06:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.