{"id":28547064,"url":"https://github.com/skywind3000/emake","last_synced_at":"2025-07-07T17:31:17.597Z","repository":{"id":25096159,"uuid":"28517189","full_name":"skywind3000/emake","owner":"skywind3000","description":"你见过的最简单的 GCC/CLANG 项目构建工具，定义式构建，比命令式更简单","archived":false,"fork":false,"pushed_at":"2025-03-23T05:56:37.000Z","size":418,"stargazers_count":889,"open_issues_count":11,"forks_count":119,"subscribers_count":46,"default_branch":"master","last_synced_at":"2025-06-10T00:09:05.058Z","etag":null,"topics":["autotools","buildtools","c","cmake","cpp","emake","gnu","gnumake","make","makefile","msbuild","ninja","xmake"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/skywind3000.png","metadata":{"files":{"readme":"README.md","changelog":"history.txt","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-12-26T17:46:59.000Z","updated_at":"2025-06-04T09:39:35.000Z","dependencies_parsed_at":"2024-09-12T19:44:37.908Z","dependency_job_id":"ae6413d0-9ee7-405c-a0e4-cb6b39ce7b37","html_url":"https://github.com/skywind3000/emake","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/skywind3000/emake","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skywind3000%2Femake","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skywind3000%2Femake/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skywind3000%2Femake/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skywind3000%2Femake/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/skywind3000","download_url":"https://codeload.github.com/skywind3000/emake/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skywind3000%2Femake/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264120178,"owners_count":23560504,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autotools","buildtools","c","cmake","cpp","emake","gnu","gnumake","make","makefile","msbuild","ninja","xmake"],"created_at":"2025-06-10T00:09:10.365Z","updated_at":"2025-07-07T17:31:17.588Z","avatar_url":"https://github.com/skywind3000.png","language":"Python","readme":"## Preface\r\n\r\nGNU Make 太麻烦？Makefile 写起来太臃肿？头文件依赖生成搞不定？多核同时编译不好弄？Emake 帮你解决这些问题：\r\n\r\n- 使用简单：设定源文件，设定编译参数和输出目标就行了，emake为你打点好一切。\r\n- 依赖分析：快速分析源代码所依赖的头文件，决定是否需要重新编译。\r\n- 输出模式：可执行、静态库（.a）、动态库（.so/.dll）。\r\n- 多核编译：轻松实现并行编译，加速项目构建。\r\n- 精简紧凑：只有唯一的一个 emake.py 文件。\r\n- 干净利索：无需导出 Makefile/.sln 等中间文件，构建一步到位。\r\n- 交叉编译：构建 iOS 项目 ，安卓项目，等等。\r\n- 语言支持 `C` / `C++` / `ObjC` / `ObjC++` / `ASM`\r\n- 工具支持 `gcc` / `mingw` / `clang` \r\n- 运行系统 `Windows` / `Linux` / `Mac OS X` / `FreeBSD` \r\n- 信息导出：项目文件列表，目标文件，以及 `compile_commands.json` 等。\r\n- 包管理支持 `pkg-config`，`vcpkg` 和手工等三种方式导入第三方包。 \r\n- 方便的交叉编译，轻松构建 `Android NDK` / `iOS` / `asm.js` 项目。\r\n- 你见过最简单的构建系统，比 Gnu Make / CMake 都简单很多。\r\n\r\n只有两三个源代码，那 makefile 随便写，文件一多，搞依赖都可以搞死人。emake 就是简单中的简单，不但比 GNU Make 简单，还要比 cmake 简单很多。\r\n\r\n应为 CMake 和 GNU Make 都是**命令式构建工具**，而 emake 是**定义式构建工具**，命令式当然可以处理各种复杂情况，本身就是一门编程语言，强大却失之复杂，而定义式类似 IDE 那样，设定文件，编译参数链接参数，就能开始工作了，虽然做不到命令式那么灵活，但能满足大多数中小型项目开发，个人实验项目的日常开发。\r\n\r\nEmake 是为快速开发而生的，通过牺牲了部分灵活性，却换来了极大的便利性，最初版本在 2009年发布，多年间团队在不同操作系统下用它构建过：服务端项目、客户端项目、iOS项目、安卓项目 和 Flash项目，这些项目都稳健的跑在生产环境中，为海量用户提供服务。\r\n\r\n多年的开发中，emake 提高了各种大小项目的开发效率，自身也随着时间增加不断被完善和稳定。\r\n\r\n具体好用在哪里，实际使用比 cmake/xmake 简单在哪里？可以看这个介绍：\r\n\r\n[Emake：你见过最简单的 C/C++ 构建工具](https://skywind.me/blog/archives/2768)。\r\n\r\n\r\n## Content\r\n\r\n- [Preface](#preface)\r\n- [Content](#content)\r\n- [Install](#install)\r\n    - [Linux / Mac OS X](#linux--mac-os-x)\r\n    - [Windows](#windows)\r\n- [快速开始](#快速开始)\r\n    - [增加编译选项](#增加编译选项)\r\n    - [完整例子](#完整例子)\r\n    - [零工程文件](#零工程文件)\r\n    - [绝对路径](#绝对路径)\r\n    - [使用技巧](#使用技巧)\r\n- [工程配置说明](#工程配置说明)\r\n    - [添加源代码](#添加源代码)\r\n    - [目录设置](#目录设置)\r\n    - [连接静态库](#连接静态库)\r\n    - [目标格式](#目标格式)\r\n    - [临时目录](#临时目录)\r\n    - [条件编译](#条件编译)\r\n    - [编译配置](#编译配置)\r\n    - [细粒度参数](#细粒度参数)\r\n    - [事件机制](#事件机制)\r\n- [工具链配置](#工具链配置)\r\n    - [配置格式](#配置格式)\r\n    - [配置项目](#配置项目)\r\n    - [配置导入](#配置导入)\r\n    - [系统包管理](#系统包管理)\r\n- [启动参数](#启动参数)\r\n- [快速开发](#快速开发)\r\n- [输出信息](#输出信息)\r\n- [TODO](#todo)\r\n\r\n\r\n## Install\r\n\r\n#### Linux / Mac OS X\r\n\r\n```bash\r\nwget http://skywind3000.github.io/emake/emake.py\r\nsudo python emake.py -i\r\n```\r\n\r\n运行上面两条指令，十秒内完成安装。emake 会拷贝自己到 /usr/local/bin 下面，后面直接使用 emake 指令操作。\r\n\r\n#### Windows\r\n\t\r\n下载 emake.py，放到你的 mingw 根目录下（便于 emake 定位 gcc），并且添加到 PATH 环境变量，同级目录新建立一个 emake.cmd 文件，内容如下：\r\n\r\n```batch\r\n@echo off\r\nd:\\dev\\python311\\python.exe d:\\dev\\mingw\\emake.py %*\r\n```\r\n\r\n修改一下对应路径即可，建立这个 emake.cmd 的批处理文件是为了方便每次敲 emake 就可以工作，避免敲 \"python emake.py\" 一长串。\r\n\r\n## 快速开始\r\n\r\n假设你有三个文件：foo.c, bar.c, main.c 共同编译成名字为 main(.exe) 的可执行文件，我们创建 “main.mak” 文件：\r\n\r\n```make\r\n; 指明目标格式：exe, lib, dll 三选一\r\nmode: exe\r\n\r\n; 加入源文件\r\nsrc: foo.c\r\nsrc: bar.c\r\nsrc: main.c\r\n```\r\n\r\n是不是比 makefile, cmake 之类的步骤简单多了？编译项目：\r\n\r\n```bash\r\nemake main.mak\r\n```\r\n\r\n好了，工程顺利编译成功，每次任何一个文件发生变动，相关对其依赖的源文件都会重新编译，而无依赖的代码则不需要再次编译。\r\n\r\n#### 增加编译选项\r\n\r\n如果需要增加编译选项的话：\r\n\r\n```make\r\n; 指明目标格式：exe, lib, dll 三选一\r\nmode: exe\r\n\r\n; 编译选项\r\nflag: -Wall, -O3, -g\r\n\r\n; 加入源文件\r\nsrc: foo.c\r\nsrc: bar.c\r\nsrc: main.c\r\n\r\n```\r\n\r\n如果项目中使用了数学库 libm.a的话：\r\n\r\n```make\r\nlink: m\r\n```\r\n\r\n如果还是用了 libstdc++.a 的话：\r\n\r\n```make\r\nlink: m, stdc++\r\n```\r\n\r\n或者：\r\n\r\n```make \r\nlink: m\r\nlink: stdc++\r\n```\r\n\r\nlink 可以直接写 .a 库的文件名：\r\n\r\n```make\r\nlink: ./lib/libmylib.a\r\n```\r\n\r\n如果需要添加额外的 include 目录 和 lib 目录的话：\r\n\r\n```make\r\ninc: /usr/local/opt/jdk/include\r\nlib: /usr/local/opt/jdk/lib\r\n```\r\n\r\n还可以手动指定输出的文件名：\r\n\r\n```make\r\nout: main\r\n```\r\n\r\n手动指定临时文件夹，避免临时 .o 文件污染当前目录的话：\r\n\r\n```make\r\nint: objs\r\n```\r\n\r\n这样所有的临时文件就会跑到 objs 目录下面了，想要清理的话，删除  objs目录即可。\r\n\r\n#### 完整例子\r\n\r\n```make\r\n; 指明目标格式：exe, lib, dll 三选一\r\nmode: exe\r\n\r\n; 编译选项\r\nflag: -Wall, -O3, -g\r\n\r\n; 设定链接\r\nlink: m, pthread, stdc++\r\n\r\n; 额外头文件路径\r\ninc: /usr/local/opt/jdk/include\r\ninc: /usr/local/opt/jdk/include/linux\r\n\r\n; 额外库文件路径\r\nlib: /usr/local/opt/jdk/lib\r\n\r\n; 加入源文件\r\nsrc: foo.c\r\nsrc: bar.c\r\nsrc: main.c\r\n```\r\n\r\n#### 零工程文件\r\n\r\n写工程文件还是觉得累？没关系，上面所有工程配置都可以用 **docstring** 的方式写在源文件里，使用 `//!` 开头的注释中，比如 main.cpp 里：\r\n\r\n```cpp\r\n#include \u003cstdio.h\u003e\r\n\r\n//! mode: exe\r\n//! flag: -Wall, -O3, -g\r\n//! link: m, pthread\r\n//! src: foo.c, bar.c\r\nint main(void) {\r\n    ...\r\n    return 0;\r\n}\r\n```\r\n\r\n源文件中 `//!` 开头的特殊注释会被 emake 提取，作为工程配置的内容。这个例子基本揽括了上面独立工程文件做的事情，当然 `src:` 部分 main.cpp 是会被自动加入的，不用额外写，然后构建时：\r\n\r\n```bash\r\nemake main.cpp\r\n```\r\n\r\n即可，简单到了极点，特别适合验证一些小想法，做一些小实验，不用写一大堆乱七八糟的东西。\r\n\r\n#### 绝对路径\r\n\r\n为了避免有时编译错误输出时，文件名用的相对路径，造成外部编辑器无法正确解析，可以用 `--abs` 参数：\r\n\r\n```bash\r\nemake --abs main.cpp\r\n```\r\n\r\n这样错误输出中的文件名，就是绝对路径了。\r\n\r\n#### 使用技巧\r\n\r\n- [如何 vcpkg 集成到 emake 中？](https://github.com/skywind3000/emake/wiki/%E5%A6%82%E4%BD%95-vcpkg-%E9%9B%86%E6%88%90%E5%88%B0-emake-%E4%B8%AD%EF%BC%9F)\r\n- [如何进行交叉编译?](https://github.com/skywind3000/emake/wiki/%E5%A6%82%E4%BD%95%E9%85%8D%E7%BD%AE%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91%EF%BC%9F)\r\n- [如何在 Windows 项目中添加 RC 文件？](https://github.com/skywind3000/emake/wiki/%E5%A6%82%E4%BD%95%E5%9C%A8-Windows-%E9%A1%B9%E7%9B%AE%E4%B8%AD%E6%B7%BB%E5%8A%A0-RC-%E6%96%87%E4%BB%B6%EF%BC%9F)\r\n\r\n更多技巧见 “[FAQ：常见问题集](https://github.com/skywind3000/emake/wiki/Frequently-Asked-Questions)” 的 wiki 页面。\r\n\r\n\r\n## 工程配置说明\r\n\r\nEmake 的工程文件里面支持下面几种核心设置：\r\n\r\n| 名称 | 含义 |\r\n|-|-|\r\n| src | 指定项目源文件，逗号分割，支持通配符 |\r\n| inc | 指定 include 目录，逗号分割，支持相对路径（相对于工程文件）|\r\n| lib | 指定库文件目录，格式同上 |\r\n| link | 指定库文件，逗号分割，比如 `lib: png` 就会连接 `libpng.a` |\r\n| flag | 指定编译通用选项，逗号分割 |\r\n| cflag | 指定 C 语言的编译选项，逗号分割 |\r\n| cxxflag | 指定 C++ 的编译选项，逗号分割 |\r\n| define | 定义宏，格式为 `define: USE_UTF8=1`，可用逗号间隔多个宏 |\r\n| mode | 设置目标格式：`exe`，`lib`, `dll`, `win` 几种 |\r\n| out | 设置目标文件名 |\r\n| int | 设置临时目录，放置 `.o` 文件和中间文件 |\r\n| flnk | 连接时传入的参数，逗号分割 |\r\n| wlnk | 连接时使用 `-Wl,` 前缀直接透传给 `ld` 的参数，逗号分割 |\r\n\r\n还支持几种辅助配置：\r\n\r\n| 名称 | 含义 |\r\n|-|-|\r\n| export | 当 mode 为 dll 时导出符号成 `.lib` 文件给 MSVC 用 |\r\n| import | 从 emake.ini 配置的非 `default` 区导入配置 |\r\n| package | 从 pkg-config 导入某个包配置，并设置 inc/lib 目录和 link 选项 |\r\n| pcflag | 调用 pkg-config 时的参数 |\r\n| echo | 输出文字 |\r\n| color | 设置 echo 的颜色 |\r\n| preload | 事件：加载前，后接 shell 命令 |\r\n| prebuild | 事件：编译前，后接 shell 命令 |\r\n| prelink | 事件：连接前，后接 shell 命令 |\r\n| postbuild | 事件：构建后，即连接成功了就调用，后接 shell 命令 |\r\n| environ | 设置环境变量 |\r\n\r\n下面对其中几项略作说明。\r\n\r\n#### 添加源代码\r\n\r\n用于声明项目里面的源文件，格式：\r\n\r\n```make\r\nsrc: file1\r\nsrc: file2\r\n...\r\nsrc: filen\r\n```\r\n\r\n或者：\r\n\r\n```make\r\nsrc: file1, file2, file3\r\nsrc: file4, file5, file6\r\n```\r\n\r\n也可以带通配符：\r\n\r\n```make\r\nsrc: core/*.c\r\nsrc: source/*.cpp\r\n```\r\n\r\n#### 目录设置\r\n\r\n声明项目中的 include 文件夹，相当于 gcc 的 `-I` 命令：\r\n\r\n```make\r\ninc: dir1\r\ninc: dir2\r\n```\r\n\r\n或者：\r\n\r\n```make\r\ninc: dir1, dir2\r\n```\r\n\r\n和 src 一样可以使用逗号分隔；而 `lib` 选项用于设置库文件目录，相当于 gcc 的 `-L` 命令，格式和 `inc` 类似。\r\n\r\n#### 连接静态库\r\n\r\n添加需要链接的库，相当于 gcc 的 -l 指令：\r\n\r\n```make\r\nlink: m, pthread, stdc++\r\n```\r\n\r\n或者：\r\n\r\n```make\r\nlink: m\r\nlink: pthread\r\nlink: stdc++\r\n```\r\n\r\n同时支持单行和多行模式，编译 C++ 项目别忘记链接 stdc++。\r\n\r\n#### 目标格式\r\n\r\n目标文件的输出格式：\r\n\r\n```make\r\nmode: [exe|lib|dll|win]\r\n```\r\n\r\n- exe: 生成可执行文件\r\n- lib: 生成静态链接库\r\n- dll: 生成动态链接库\r\n- win: Windows 下特有，生成无 console 窗口的 Windows 程序。\r\n\r\n#### 临时目录\r\n\r\n指定中间临时文件目录，一般设置为：\r\n\r\n```make\r\nint: objs\r\n```\r\n\r\n或者：\r\n\r\n```make\r\nint: objs/$(target)\r\n```\r\n\r\n指定了以后，可以避免项目中间文件污染当前目录。\r\n\r\n#### 条件编译\r\n\r\n在 emake.ini 中可以指明一系列 name，比如：\r\n\r\n```ini\r\n[default]\r\nname=android,posix,arm,nossl\r\n```\r\n\r\n每个名字代表一个条件，可以同时定义多个条件，然后在工程文件里使用：\r\n\r\n```text\r\n\u003cname\u003e/option: value\r\n```\r\n\r\n来声明，只有 emake.ini 的 name 里包含特定内容时，才会触发后面内容，比如：\r\n\r\n```make\r\nwin32/link: pdcurses_wincon\r\nlinux/link: ncurses, tinfo\r\narm/src: arm_calculate.c\r\n```\r\n\r\n这两条语句代表不同平台 link 不同的库，`name` 可以不定义，它的默认值是 target，你可以只定义一个 target 不定义 name：\r\n\r\n```ini\r\n[default]\r\ntarget=android\r\n```\r\n\r\n这样手工指明目标平台的名称，默认的话，会使用 python 里的 `sys.platform` 返回值。同时你又没有定义过 `name` 项目，那么 `name` 的默认值就是 `target`，除非你和上面一样手工定义 `name`，就能覆盖默认值。\r\n\r\n所以 `name` 只有一个时，一般不定义，用默认值即可，但有多个 `name` 时需要定义下。\r\n\r\n#### 编译配置\r\n\r\n在启动 emake 时可以设置一个 `--profile` 参数来指定 debug/release 等不同的构建配置：\r\n\r\n```bash\r\npython emake.py --profile=debug main.mak\r\n```\r\n\r\n而在工程文件里，用 `@` 符号来指定某个配置是属于什么 profile 的，比如：\r\n\r\n```make\r\nflag@debug: -g, -Og\r\nflag@release: -O2\r\nflag@minsize: -Os\r\nflnk@minsize: -s\r\n```\r\n\r\n那么 emake 就会根据命令行传入的 profile 选择对应的配置项目，再比如，给不同配置规定不同的目标文件名：\r\n\r\n```make\r\nwin32/out: rogue-clone.exe\r\nwin32/out@debug: rogue-cloned.exe\r\nlinux/out: rogue-clone\r\nlinux/out@debug: rogue-cloned\r\n```\r\n\r\n这里分别演示了在不同平台下，不同 profile 可以指定不同的配置。\r\n\r\n#### 细粒度参数\r\n\r\nEmake 支持为每个源代码文件设置不同的编译参数，格式是就是在 `src` 定义完源文件后，右边加冒号跟着参数即可：\r\n\r\n```make\r\nsrc: main.c : -O3\r\nsrc: foo.c, bar.c : -DTEST\r\n```\r\n\r\n上面第一行单独为 `main.c` 设置了 `-O3` 的参数，第二行为另外两个源文件定义了一个叫做 `TEST` 的宏。\r\n\r\n#### 事件机制\r\n\r\n根据条件运行不同 shell 命令：\r\n\r\n```\r\n# 加载前\r\npreload: echo \"preload\"\r\n\r\n# 编译前\r\nprebuild: echo \"prebuild\"\r\n\r\n# 链接前\r\nprelink: echo \"prelink\"\r\n\r\n# 编译后\r\npostbuild: echo \"postbuild\"\r\n```\r\n\r\n其中 `preload` 和 `prebuild` 之间的区别是，`preload` 每次都会运行，并且是在解析项目文件和分析依赖之前运行，而 `prebuild` 是在解析项目文件分析依赖之后运行，如果二进制没更新，`prebuild` 就不会被调用，而 `preload` 每次都无条件被执行。\r\n\r\n比如某个源代码是使用其他工具生成的，放到 `prebuild` 时，应为处于依赖分析之后，初次构建尚未触发生成代码前面依赖分析就会报找不到文件了，但 `preload` 在依赖分析之前运行。\r\n\r\n在事件运行时，可以从环境变量中读取一些信息：\r\n\r\n- `$EMAKE_MAIN`: 工程文件路径。\r\n- `$EMAKE_OUT`: 输出文件路径。\r\n- `$EMAKE_MODE`: 模式，可执行还是动态库。\r\n- `$EMAKE_SCRIPT`: 脚本（emake.py）的路径。\r\n- `$EMAKE_HOME`: 工程文件所在目录，即 `$EMAKE_MAIN` 的 dirname。\r\n- `$EMAKE_INT`: 中间文件的目录。\r\n- `$EMAKE_TOOLCHAIN`: 工具链的目录。\r\n- `$EMAKE_TARGET`: 目标平台。\r\n\r\n比如 Windows 下面：\r\n\r\n```make\r\npostbuild: echo %EMAKE_OUT%\r\n```\r\n\r\n就能每次编译后显示输出文件的路径了。\r\n\r\n最后这些事件命令会在工程文件所在的目录被启动。\r\n\r\n\r\n## 工具链配置\r\n\r\nEmake 支持多个工具链，每个工具链使用一个 ini 进行描述，不显示指定工具链的名字的话，会到下面几个位置寻找默认工具链配置：\r\n\r\n1）在 `emake.py` 同级目录查找 `emake.ini` 。\r\n2）/etc/emake.ini \r\n3）/usr/local/etc/emake.ini\r\n4）~/.config/emake.ini\r\n\r\n默认工具链配置并不强制，没提供的话，Emake 也会到 `$PATH` 中寻找 gcc 等工具并自动设置。\r\n\r\n如果想用别的工具链配置的话，使用 `--cfg=name` 参数就会加载 `~/.config/emake/{name}.ini` ：\r\n\r\n    emake --cfg=mingw64 \u003cparameters\u003e\r\n\r\n这样它会去试图加载 `~/.config/emake/mingw64.ini`，也可以用 `--ini=path` 直接给 ini 文件绝对路径：\r\n\r\n\temake --ini=/absolute/path/to/name.ini \u003cparameters\u003e \r\n\r\n通常使用 `--cfg=name`，并在 `~/.config/emake` 目录下统一管理所有配置，比如你有多套工具链，每个工具链一个配置放进去，用起来比较方便，特别是有交叉编译的情况时。\r\n\r\n#### 配置格式\r\n\r\n工具链配置文件的内容类似：\r\n\r\n```ini\r\n[default]\r\n# 工具链的 bin 目录，用于查找 gcc / clang 等工具\r\nhome=d:/msys32/mingw32/bin\r\n# 当你有多套工具链时，不可能都加入 $PATH，这个配置可以让 emake 在\r\n# 构建时临时追加到 $PATH 前面，不污染外层父进程的环境变量\r\npath=d:/msys32/mingw32/bin,d:/msys32/usr/bin\r\n\r\n# 设置传参模式，如果使用 clang，则取消下面注释\r\n# option=x\r\n\r\n# 通用配置，免得每个工程文件写一遍\r\nflag=-Wall\r\nlink=stdc++, winmm, wsock32, user32, ws2_32\r\ncflag=-std=c11\r\ncxxflag=-std=c++17\r\n\r\n# 针对 debug/release/static 三种 profile 的设置，使用\r\n# emake --profile=\u003cname\u003e xxx 在构建时指明使用啥 profile\r\ndefine@debug=_DEBUG=1\r\ndefine@release=_RELEASE=1\r\ndefine@static=_STATIC=1, _RELEASE=1\r\n\r\nflag@debug=-Og, -g, -fno-omit-frame-pointer\r\nflag@release=-O3\r\nflag@static=-O3, -static\r\n\r\n# 多核编译\r\ncpu=4\r\n\r\n# 目标平台名称，不提供得话默认用 python 的 sys.platform 字符串代替\r\ntarget=win32\r\n\r\n# 条件编译时候的条件变量，在工程文件里可以用 win32/flag: xxx 来使用\r\nname=win32,nt,have_openssl\r\n```\r\n\r\n配置都包括在 ini 的 `[default]` 区，其中第一行使用 `home` 定义了工具链的 `bin` 目录，在哪里将找到 `gcc`, `ar`, `as`, `ld` 等工具，可以用绝对路径，也可以用相对于 ini 文件的路径。\r\n\r\n接下来使用 `path` 定义了调用工具链前需要把哪些目录追加到系统变量 `$PATH` 前，使用逗号分割。\r\n\r\n中间就是一些通用参数，和针对 `debug`, `release` 和 `static` 三种 profile 的不同设置，然后用 `cpu=4` 指定了同时使用四核编译。\r\n\r\n最后用 `target` 声明目标平台的名字是 `win32`。\r\n\r\n这样的 ini 配置文件除了上面默认的位置加载外，还可以手工指定。\r\n\r\n\r\n#### 配置项目\r\n\r\n工具链配置文件的 `[default]` 区支持下面几种配置：\r\n\r\n| 名称 | 含义 |\r\n|-|-|\r\n| home | 工具链的 bin 目录，下面可以找到 gcc 和 ld 等工具，可以用绝对路径或相对于 ini 文件的路径，不提供的话会搜索 `$PATH` 中的 gcc，比如默认工具链配置可以不写，如果 gcc 刚好在你的 `$PATH` 中的话 |\r\n| gcc |  gcc 工具的可执行文件名，默认不提供的话，就会试图去调用 \"gcc\"，如果不是这个名字的话，可以用该选项配置一下，比如设置成 \"clang\" 可以调用 clang，而很多交叉编译工具链前面一般有一串前缀，比如 \"arm-linux-androideabi-gcc\" 这也，也需要设置 |\r\n| ar | 静态库工具名，同上 |\r\n| ld | 连接器工具名，同上 |\r\n| as | 汇编器工具名，同上 |\r\n| dllwrap | 动态库封装工具名，同上 |\r\n| pkgconfig | pkgconfig 工具名，默认为 `pkg-config` |\r\n| path | 调用工具时额外需要加入 `$PATH` 的路径，用 `,` 分割多个项目；很多工具链的 bin 目录并不会加入系统的 `$PATH`，但里面的工具又会互相调用，使用这个选项可以不污染外部环境变量，只有在 emake 进程里临时设置 `$PATH` 变量，干净一些 |\r\n| option | 扩展配置字符串，如果使用 clang，需要设置 `option=x` 模式，因为 lld 和 ld 的传参有一些不一样的地方，不能使用 `-Xlinker` 模式 |\r\n| cpu | 后面跟一个数字，多核编译的核心数量 |\r\n| target | 目标平台名称，比如 `win32` 和 `linux`，不提供的话会使用 Python 的 `sys.platform` 作为默认值 |\r\n| name | 条件编译的条件变量名称，逗号分割，比如 \"android,posix,nossl\"，注意 target 的值会自动加入到 name 中，方便根据目标平台进行条件判断  |\r\n| environ | 逗号分隔的环境变量，比如 `environ=FOO=BAR,BARZ=1`，调用工具链前初始化 |\r\n| pcpath | 设置环境变量 `$PKG_CONFIG_PATH` |\r\n| pcflag | 设置 `pkg-config` 的公用参数 |\r\n\r\n上面的配置用于确定工具链的位置和运行方式，下面这些用于通用项目配置\r\n\r\n| 名称 | 含义 |\r\n|-|-|\r\n| include | 额外的全局 include 目录，逗号分割，类似项目配置里的 `inc` 选项，如果有一些多个项目都需要设置的 include 目录，可以把它设置到工具链配置里，这也就不用每个项目都写了 |\r\n| lib | 额外的全局 lib 目录，逗号分割，类似项目配置里的 `lib` 选项 |\r\n| flag | 全局编译参数，类似项目配置里的 `flag` 选项，编译具体项目时，会添加到项目配置的 flag 前面 |\r\n| cflag| 全局 C 语言编译参数，类似项目配置里的 `cflag` 选项 |\r\n| cxxflag | 全局 C++ 编译参数，类似项目配置里的 `cxxflag` 选项 |\r\n| link | 全局挂载库，比如一些所有项目都要挂载的库 `pthread`, `stdc++` 之类的，写在这里，就不用每个项目都写 |\r\n| define | 全局宏定义 |\r\n| flnk | 连接时传输的参数 |\r\n| wlnk | 连接时使用 `-Wl,` 前缀直接透传给 `ld` 工具的参数 |\r\n\r\n\r\n#### 配置导入\r\n\r\n在工具链配置的 ini 文件里，在非 `[default]` 区可以写一些额外配置：\r\n\r\n```ini\r\n[default]\r\n...\r\n\r\n[ffmpeg]\r\ninclude=d:/dev/local/opt/ffmpeg/include\r\nlib=d:/dev/local/opt/ffmpeg/lib\r\nlink=avcodec, avdevice, avfilter, avformat, avutil, postproc, swscale\r\n\r\n[qt]\r\ninclude=D:/Dev/Qt/sdk/4.8.3-mingw/include;D:/Dev/Qt/sdk/4.8.3-mingw/include/QtGui\r\nlib=D:/Dev/Qt/sdk/4.8.3-mingw/lib\r\nlink=stdc++, ole32, gdi32, wsock32, opengl32, gdi32, glu32, ws2_32, uuid, oleaut32, winmm, imm32, winspool, QtCore4, QtGui4, QtGuid4\r\n\r\n```\r\n\r\n那么在项目配置里就可以使用 import 来导入：\r\n\r\n\timport: qt, ffmpeg\r\n\r\n那么在你的工程里，上面 qt 和 ffmpeg 的相关配置就会被导入了。\r\n\r\n#### 系统包管理\r\n\r\n要引入一个包的话，除了用上面的配置导入外，还有一种方式是用 pkg-config，只需要在工程文件里用 `package` 语句即可：\r\n\r\n```make\r\npackage: python3\r\n```\r\n\r\n那么 Emake 会在构建前调用 `pkg-config` 查询 `python3` 这个包的各种编译信息，包括 CFLAGS, LDFLAGS 等，并追加到工程配置中。\r\n\r\n当然 pkg-config 依赖 `/usr/lib` 或者 `/usr/local/bin` 下面的 `.pc` 文件，比如：\r\n\r\n```text\r\n./lib/x86_64-linux-gnu/pkgconfig/libxcrypt.pc\r\n./lib/x86_64-linux-gnu/pkgconfig/lua.pc\r\n./lib/x86_64-linux-gnu/pkgconfig/formw.pc\r\n./lib/x86_64-linux-gnu/pkgconfig/lua54.pc\r\n./lib/x86_64-linux-gnu/pkgconfig/lua5.4-c++.pc\r\n./lib/x86_64-linux-gnu/pkgconfig/python3.pc\r\n./lib/x86_64-linux-gnu/pkgconfig/ncurses++.pc\r\n./lib/x86_64-linux-gnu/pkgconfig/lua-5.4.pc\r\n```\r\n\r\n这些 `.pc` 文件是在安装系统包或者编译第三方包时 `make install` 或者 `cmake --install` 命令生成的，主要用于指定各个包的 `CFLAGS` / `LDFLAGS` 等编译参数以及依赖关系。\r\n\r\n而 `pkg-config` 工具，能够加载这些文件处理好依赖并给出正确的 `CFLAGS` / `LDFLAGS` 编译参数。此外，在工程文件里，还可以给出 `pkg-config` 工具调用的额外参数：\r\n\r\n```make\r\npcflag: --atlatest-version\r\n```\r\n\r\n这也这个参数就会传递给 `pkg-config`。\r\n\r\n还可以在工具链配置文件中，指定 `$PKG_CONFIG_PATH` 这个环境变量：\r\n\r\n```ini\r\n[default]\r\n...\r\npcpath=D:/arm32-devkit/lib/pkgconfig\r\npcflag=--env-only\r\n...\r\n```\r\n\r\n这样调用 `pkg-config` 前会先设置这个环境变量，控制 `pkg-config` 搜索 `.pc` 文件的位置，当然可以配合下一条 `pcflag=--env-only` 跳过默认搜索路径。\r\n\r\n通常情况下不用特别设置 `pcpath`，因为工具链自带的 `pkg-config` 一般能够根据自身位置正确搜索到工具链内的 `.pc` 文件的，除非你还有其他位置自己管理额外的 `.pc` 或者工具链不提供 `pkg-config` 工具，你需要依赖一个外部的 `pkg-config` 程序。\r\n\r\n这是推荐的包导入方式，而如果一个包没有 `.pc` 文件，`pkg-config` 找不到的话，可以用上面的 `import` 方式导入一个工具链 ini 配置文件里的 section。\r\n\r\n## 启动参数\r\n\r\n```text\r\nusage: \"emake.py \u003caction\u003e [options] srcfile\" (emake 3.7.3 Aug.27 2024 win32)\r\n\r\nactions  :  -b | -build      build project\r\n            -c | -compile    compile project\r\n            -l | -link       link project\r\n            -r | -rebuild    rebuild project\r\n            -e | -execute    execute project\r\n            -o | -out        show output file name\r\n            -d | -cmdline    call cmdline tool in given environ\r\n            -g | -cygwin     cygwin execute\r\n            -s | -cshell     cygwin shell\r\n            -i | -install    install emake on unix\r\n            -u | -update     update itself from github\r\n            -h | -help       show help page\r\n\r\n            -home            display project home\r\n            -list            display project files\r\n            -objs            display obj files\r\n            -cflags          display compile flags\r\n            -depends         display dependencies\r\n            -dirty           display dirty files\r\n            -commands        display compile commands json\r\n\r\noptions  :  --cfg={cfg}      load config from ~/.config/emake/{cfg}.ini\r\n            --ini={inipath}  load config from {inipath} directly\r\n            --profile={name} set profile to {name}\r\n            --print={n}      set verbose level: 0-3\r\n            --abs={0|1}      display absolute path in error messages\r\n\r\nEmake is a easy tool which controls the generation of executables and other\r\nnon-source files of a program from the program's source files.\r\n```\r\n\r\n## 快速开发\r\n\r\n不管时 GNU Make 还是 cmake，亦或时其他构建系统，都需要你写一个专门的工程文件来描述该工程。对于大项目很正常，但是对于中小项目，特别时一些测试类项目，这真的太麻烦了。\r\n\r\nEmake 可以不用工程文件，而将工程配置信息嵌入到源代码的注释中：\r\n\r\n```cpp\r\n#include \u003cstdio.h\u003e\r\n#include \u003cstddef.h\u003e\r\n#include \"foobar.h\"\r\n\r\n\r\n//! mode: exe\r\n//! src: foo.cpp, bar.cpp, utils.cpp\r\nint main(void)\r\n{\r\n\tprintf(\"Hello, World !!\\n\");\r\n\tfoo();\r\n\tbar();\r\n\treturn 0;\r\n}\r\n\r\n```\r\n\r\n这样在你的源文件里面增添两行以后，即可使用：\r\n\r\n\temake main.cpp\r\n\r\n来进行编译，emake 会自动提取 `//!` 开头的注释，解析为 emake的项目描述信息，上面的配置描述了该项目依赖的文件（除了 main.cpp自己外），以及项目模式为生成可执行文件。\r\n\r\n这样写起来，比所有构建系统都简单很多。\r\n\r\n## 输出信息\r\n\r\n列出项目最终输出文件，比如 `xxx.so` 之类：\r\n\r\n```bash\r\nemake -o hello.mak\r\n```\r\n\r\n列出项目所包含的源文件：\r\n\r\n```bash\r\nemake -list hello.mak\r\n```\r\n\r\n列出编译参数：\r\n\r\n```bash\r\nemake -cflags hello.mak\r\n```\r\n\r\n列出依赖：\r\n\r\n```bash\r\nemake -depends hello.mak\r\n```\r\n\r\n输出 `compile_commands.json` 内容:\r\n\r\n```bash\r\nemake -commands hello.mak\r\n```\r\n\r\n还有更多信息，具体见：`emake -h` 说明。\r\n\r\n## TODO\r\n\r\n- [ ] 加入 PIP，支持 pip 一键安装。\r\n\r\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskywind3000%2Femake","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskywind3000%2Femake","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskywind3000%2Femake/lists"}