最近在开发项目中遇到一个很奇怪的问题:在源文件没有任何修改的情况下,每次使用visual studio(2020预览版)启动程序调试前,项目都会被重新编译一遍。这一度让我怀疑是最新版本预览版IDE的隐藏的问题,但是仔细思考又不太可能,于是基于一个没有出现这个问题的commit创建新的测试分支,对比验证没有出现类似问题,排除IDE的问题。最终对比commit提交历史,发现端倪,定位问题根本原因是引入的新库文件时间戳出现问题。(旧版本nuget-4.1.0打包工具在最新的visual studio 2019和2022下解包库相关文件时间戳出现问题,使用最新的nuget-5.1.0版本无此问题)
一、Build Solution & Rebuild Solution & Clean Solution之间的区别
Build Solution : 执行增量型构建。如果编译器认为代码文件未改变,就不会重写编译;否则需要进行编译。
Clean Solution : 删除所有之前编译的文件。
Rebuild Solution : 删除所有之前编译的文件,即:Clean;然后重写编译,不管代码文件是否由更改。
1.1.两种编译过程流程图
1.2.Build Solution执行过程:
1.3.Rebuild Solution执行过程:
二、文件时间戳带来的编译问题
2.1.编译器如何检测代码文件改变呢?——文件时间戳
新建一个源代码文件*.h;*.cpp并添加代码:
Build后的*.obj文件时间戳如下:
修改后编译的源码文件:
构建生存的对象文件:
文件不做任何修改,进行Build:
Clean:
clean前:
clean后:
*.exe和*.obj文件被删除。
2.2.手动修改时间戳
2.2.1.源码不变,时间戳改为过去
前:
后:
未进行重写编译。
2.2.2.源码不变,时间戳改为将来
前:
后:
第一次Build:
第二次Build:
只有代码文件修改时间戳在当前系统时间戳前,不管是否修改源文件,都会认为文件被修改,重写编译。
2.2.3.源码修改,时间戳改为过去
前:
后:
不会被重写编译。
在正常编译代码中,修改文件的时间戳是系统自动计算的,一般编译过一次,不修改后,再次编译一般不会再重写编译;但是不保证一些包管理器由于版本问题,导致解析过程中出现时间戳超前(如最开始导读部分的问题),会导致每次都会重写编译,严重影响到项目开发进度。
三、给编码带来的一些启示
- 尽量减少公共头文件,因为稍稍修改下这类头文件内容,那些包含头文件的其他源文件都会被重写编译;可以将一些特属于某个类或命名空间的的常量,枚举单独定义在头文件或定义在*.cpp(*.cc)文件中的。
- 编译防火墙:定义接口类,使用指针组合对应的实现类,实现类单独定义和实现。这样,修改实现类的源文件,只会重写编译修改的文件,其他包含接口类的文件不必重写编译。
参考:
- https://*.com/questions/3095901/difference-between-build-solution-rebuild-solution-and-clean-solution-in-visua
- https://bitwizards.com/thought-leadership/blog/2014/august-2014/visual-studio-why-clean-build-rebuild
- https://www.cppstories.com/2018/01/pimpl/