概念:dependencies, targets, rules
make和Makefile
- 根据modify time确定什么文件需要regenerate
target: prerequisite1 prerequisite2 ...
command1
command2 (需要tab)paper.pdf: paper.tex plot-data.png
pdflatex paper.tex
plot-%.png: %.dat plot.py
./plot.py -i $*.dat -o $@# specify all source files here
SRCS = hw.c helper.c
# specify target here (name of executable)
TARG = hw
# specify compiler, compile flags, and needed libs
CC = gcc
OPTS = -Wall -O
LIBS = -lm
# this translates .c files in src list to .o’s
OBJS = $(SRCS:.c=.o)
# all is not really needed, but is used to generate the target, default directive
all: $(TARG)
# this generates the target executable
$(TARG): $(OBJS)
$(CC) -o $(TARG) $(OBJS) $(LIBS)
# this is a generic rule for .o files
%.o: %.c
$(CC) $(OPTS) -c $< -o $@
# and finally, a clean line
.PHONY: clean
clean:
rm -f $(OBJS) $(TARG)- 第一个directive是default goal
- indent后面的命令是建立依赖的语句
- .PHONY避免clean和名为clean的文件冲突
SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
foo: baz- 配合git ls-files,写make的标准targets
- 可以利用
.git/hooks中的pre-commit在每次commit之前make特定的文件
概念:repository, versioning, version number, semantic versioning
- If a new release does not change the API, increase the patch version.
- If you add to your API in a backwards-compatible way, increase the minor version.
- If you change the API in a non-backwards-compatible way, increase the major version.
这三条是入门概括。严格说,patch 对应兼容的 bug 修复;“公共 API 没变”并不意味着只能升 patch,重大内部功能或改进也可升 minor。完整规则见下文。
- Rust's build system,可帮助理解版本号以及dependency管理
lock file: a file that lists the exact version you are currently depending on of each dependency
- vendoring: copy all the code of your dependencies into your own project
makedepend工具能帮助寻找依赖
来源:Semantic Versioning 2.0.0、中文译文、FAQ。以下按英文规范归纳;原规范由 Tom Preston-Werner 发起,采用 CC BY 3.0。
SemVer 用版本号传递公共 API 的兼容性变化,让依赖方判断升级范围。前提是先明确公共 API;代码或文档都可以定义契约,但边界应精确、完整。版本数字本身不能证明兼容性。
标准格式为 MAJOR.MINOR.PATCH,三项均为非负整数、不能带前导零,按数值递增,如 1.9.0 → 1.10.0。对稳定版本(major > 0):
| 变更 | 版本处理 | 从 2.4.7 出发的例子 |
|---|---|---|
| 仅修复错误,保持向后兼容 | 必须升 patch | 2.4.8 |
| 增加兼容的公共功能,或将公共功能标记为 deprecated | 必须升 minor,patch 归零 | 2.5.0 |
| 公共 API 有不兼容变化 | 必须升 major,minor / patch 归零 | 3.0.0 |
| 私有实现有重大新功能或改进 | 可以升 minor | 2.5.0 |
minor 可以包含 patch 级变化,major 可以包含两者;同批变更中只要有不兼容 API 变化,就应按 major 发布。判断依据是契约影响,而非代码行数或改动难度。
0.y.z用于初始开发,公共 API 尚不稳定,规范允许随时变化;不能直接套用稳定版本的兼容性承诺。1.0.0表示公共 API 已界定,并以它为后续版本变化的基准。- 弃用先更新文档并发一个 minor,移除时升 major;正式移除前,至少留一个包含弃用信息的 minor,让使用者迁移。
- 已发布版本的内容不可修改,修正必须发布新版本。若误把 breaking change 发成 minor,应记录问题并发新 minor 恢复兼容,不能覆盖旧包;若恢复行为又会严重影响使用者,FAQ 建议按实际影响判断是否发 major。
- 更新依赖也看公共契约与目的:不改变公共 API 时,修 bug 与增加功能分别按 patch / minor 判断;不能仅凭“依赖版本变了”就认定为 breaking change。
工程应用:项目可把函数签名、承诺的行为、CLI、配置格式或对外数据 schema 纳入公共契约。若破坏已承诺的行为,即使签名不变也可能不兼容;未声明的内部实现则不能自动等同于公共 API。
扩展格式:MAJOR.MINOR.PATCH-prerelease+build,两个后缀均可选,例如 2.5.0-rc.1+sha.a1b2c3。
-prerelease表示先行版,可能尚未满足该正式版本预期的兼容性;它低于同一版本核心的正式版。+build是构建元数据,比较优先级时必须忽略;2.5.0+001与2.5.0+002优先级相同,不代表产物内容相同。- 两种后缀都是以点分隔的非空标识符,只能用 ASCII 字母、数字和
-;先行版的纯数字标识符不能带前导零,因此rc.01非法,构建元数据+001合法。 - 先比较 major / minor / patch 的数值,再比较先行版标识符:纯数字按数值,含字母或连字符按 ASCII;纯数字低于非数字;共同前缀相同时,标识符更多的优先级更高。
2.5.0-alpha < 2.5.0-alpha.1 < 2.5.0-alpha.beta < 2.5.0-beta
2.5.0-beta.2 < 2.5.0-beta.10 < 2.5.0-rc.1 < 2.5.0
v2.5.0 是常见 Git tag 写法,真正的 SemVer 字符串为 2.5.0,不包含 v。
若依赖了 2.4.0 新增的 API,可将兼容的正式版本范围设为 >=2.4.0, <3.0.0;范围过紧会锁死升级,过松会接受潜在的不兼容版本。^ / ~ 的含义、0.y.z 的范围策略及是否纳入先行版由具体包管理器定义,不属于 SemVer 本身。版本范围决定允许升级到哪里,lock file 固定当前实际解析的依赖;两者职责不同。
工程上,先声明契约,再用兼容性测试验证消费者行为,最后按影响发布并记录迁移路径。SemVer 是维护者的承诺,升级仍需验证,不能用“版本号允许”替代测试。接口设计与质量保障见 Software Engineering。
“stuff that runs whenever your code changes”
- e.g. Travis CI, Azure Pipelines, and GitHub Actions
- Pages is a CI action that runs the Jekyll blog software on every push to
masterand makes the built site available on a particular GitHub domain
testing
- Test suite: a collective term for all the tests
- Unit test: a “micro-test” that tests a specific feature in isolation
- Integration test: a “macro-test” that runs a larger part of the system to check that different feature or components work together.
- Regression test: a test that implements a particular pattern that previously caused a bug to ensure that the bug does not resurface.
- Mocking: the replace a function, module, or type with a fake implementation to avoid testing unrelated functionality. For example, you might “mock the network” or “mock the disk”.
- Set up a simple auto-published page using GitHub Pages. Add a GitHub Action to the repository to run
shellcheckon any shell files in that repository (here is one way to do it). Check that it works! - Build your own GitHub action to run
proselintorwrite-goodon all the.mdfiles in the repository. Enable it in your repository, and check that it works by filing a pull request with a typo in it.
个人blog的建立: