zoukankan      html  css  js  c++  java
  • vs的环境变量

    vs的环境变量

    https://zhuanlan.zhihu.com/p/34203415

    https://wiki.jikexueyuan.com/project/visual-studio

    https://blog.csdn.net/elloop/article/details/51010151

    https://blog.csdn.net/elloop/article/details/51019110

    http://elloop.github.io/tools/2016-03-28/visual-studio-predefined-macros

    http://elloop.github.io/tools/2016-03-29/using-vs-environmental-variable

    本文总结了Visual Studio中常见的环境变量及其在组织解决方案、工程中的作用。

    注:本文使用的是Visual Studio 2013,由于作者主要从事C/C++开发,所以是以Visual C++的工作环境配置来描述。

    什么是vs的环境变量?

    先看图吧,图中以美元符号$开头 + 一对括号,这样进行引用的就是我所谓的环境变量,

    图中出现的几个环境变量含义如下:

    环境变量名含义
    $(SolutionDir) 解决方案目录:即.sln文件所在路径
    $(Configuration) 当前的编译配置名称,比如Debug,或Release
    $(ProjectName) 当前项目名称,图中即为Game


    在中文版的VS中,环境变量翻译为“宏”,为了避免与C/C++语言中的宏(Macro)搞混,我在本文中把它叫做“vs环境变量”,简称环境变量。

    环境变量有什么用?

    使用环境变量来组织工程目录

    vs作为一个IDE,其天职在于帮开发者组织好工程,主要包括对工程中源文件、库文件的组织。(本质上是提供一个可视化的操作界面,让开发者方便的定义编译器和链接器的参数。)在使用vs来组织工程目录时候最常用到的两个目录是:

    • 头文件包含目录 (对应于编译器命令的:-I 参数)
    • 库文件搜索路径 (对应于编译器的:-l 参数)

    vs中默认的头文件搜索路径是: 工程路径 – 即,.vcxproj(不同版本的VC++后缀名称不同,如vs2010中后缀为.vcproj) 文件所在路径。比如如下的目录结构:

    –Root/
    —-Test.vcxproj
    —-hello.cpp
    —-hello.h
    —-world.cpp
    —-world.h
    —-main.cpp
    —-/subdir
    ——sub.h

    在Root目录包含了.vcxproj文件,所以Root就是工程路径,在vs中,这个目录下面的.h文件可以直接使用include包含进来, 比如在main.cpp中我可以写:

    #include "hello.h"
    #include "world.h"

    但是对于sub.h,我们就不能直接写#include "sub.h", 因为工程路径下面不能搜索到这个文件,我要告诉编译器这个文件在哪里,通常有以下两种方法:

    • 写成#include "subdir/sub.h"
    • 把subdir目录加入到头文件搜索路径

    Google的C++编程风格鼓励第一种做法,好处是可以看到文件相对完整的路径,如果头文件搜索路径只有一个根目录,那么这个路径就是文件的相对于根目录的物理路径,方便定位文件。

    如果你觉得这样写很麻烦,并且路径深度可能有多层,不同深度的路径下又通常包含大量的文件,那么就可以选择第二种做法,把每个子目录统统加入到搜索路径中,这样,就可以不用带着路径,直接#include "filename.h"就可以了。具体在VS中要怎么合理的添加文件包含目录呢?由此,便引出了本节问题的答案:环境变量有什么用?用途之一就是用来编写头文件的搜索路径。

    相信大家都知道如何在vs中添加一个头文件搜索路径这个常识,在此还是为初学者唠叨一下具体做法:工程属性 - 配置属性 - C/C++ - 常规 - 编辑右侧的”附加包含目录”取值即可。

    具体如下图所示:

    点击编辑之后,弹出如下图所示的编辑窗口:

    在这里可以新建、删除包含路径、调整包含顺序。

    点击新建按钮或者双击列表空白处即可添加一条包含路径,在编辑新添加的路径时,可以看到列表条目右侧有一个浏览按钮,

    点击按钮可以从打开文件对话框里选择路径,点击确定后,会看到新添加的路径名。虽然通过浏览来定位文件夹比较容易,免去了自己编写,但是你会发现,通过浏览添加的路径是绝对路径

    如果你是项目的唯一开发者,并且仅仅使用这一台电脑来开发的话,那么使用绝对路径也没什么大问题。但是如果这个项目是个团队协作项目,或者你需要在好几台电脑之间切换,那么这个包含路径如果写绝对路径就不够灵活了,如果别人的路径配置或别的电脑的路径配置不同,那么要重新修改包含路径。

    一个比较合理的编写包含路径的方法是: 使用相对路径

    相对谁呢? 相对项目根目录或者解决方案根目录。

    为什么呢?因为不管别人的电脑有什么盘符、不管别人的项目放在何处,要包含的文件都可以通过项目所在位置来计算出来。

    当然前提是,项目开发者们事先约定好被包含文件相对于项目根目录的位置。通常是放在项目根目录(或者解决方案根目录)的某个子目录里。

    具体怎么做呢?这就需要用到本文的主题:环境变量了。刚才提到的两个相对目录所对应的环境变量如下表所示:

    目录对应的环境变量名称
    项目根目录 $(ProjectDir)
    解决方案根目录 $(SolutionDir)

    要解决刚才小例子中的问题,

    –Root/
    —-Test.vcxproj
    —-hello.cpp
    —-hello.h
    —-world.cpp
    —-world.h
    —-main.cpp
    —-/subdir
    ——sub.h

    注意到.vcxproj所在目录即项目根目录,也就是$(ProjectDir)的取值等于Root/。所以要把subdir放在包含目录里,可以新建这样一条包含路径:

    $(ProjectDir)subdir

    这样,在main.cpp里就可以直接写#include "sub.h"了。不管项目被拷贝到哪里,都不用修改包含路径。

    上面就是环境变量使用的一个小例子。使用环境变量来编写文件包含路径的好处是: 包含路径独立于工程所在的路径,无论工程被移动到哪里,都不需要重新修改包含路径,因为使用环境变量来编写的文件包含路径是一种相对路径。

    其它vs环境变量

    如何查看所有的环境变量值呢?

    有好多个地方都可以查看,比如刚才在添加包含目录时候,弹出的窗口,注意其右下方,有个“宏”按钮

    点击它就能看到所有的“宏” (即vs环境变量的值):

    在上方的输入框可以进行过滤。

    下面的表格给出了常用的环境变量的含义:

    环境变量名含义
    $(SolutionDir) 解决方案目录:即.sln文件所在路径
    $(ProjectDir) 项目根目录:, 即.vcxproj文件所在路径
    $(Configuration) 当前的编译配置名称,比如Debug,或Release
    $(ProjectName) 当前项目名称
    $(SolutionName) 解决方案名称
    $(OutDir) 项目输出文件目录
    $(TargetDir) 项目输出文件目录
    $(TargetName) 项目生成目标文件, 通常和$(ProjectName)同名, 如Game
    $(TargetExt) 项目生成文件后缀名,如.exe, .lib具体取决于工程设置
    $(TargetFileName) 项目输出文件名字。比如Game.exe, 等于 $(TargetName) + $(TargetExt)
    $(ProjectExt) 工程文件后缀名,如.vcxproj

    本文将以实际例子说明如何合理使用这些环境变量来组织VC++工程。

    使用vs环境变量来组织工程

    通常一个解决方案包含多个项目,这些项目相互之间可能存在依赖关系,以下面这个解决方案为例:

    这个解决方案叫:CS.cpp, 包含了7个项目:

    项目名生成目标描述
    Algorithm .exe 算法和数据结构实践
    c_language .exe c语言实践
    TotalSTL .exe STL实践
    TrainingGround .exe C++语法自由训练场
    UnderstandingCpp11 .exe 深入理解C++11代码实践
    gtest .lib google c++单元测试框架,给其他几个项目作为测试框架
    util .lib 个人积累工具类,为其他几个项目提供util函数

    其中五个项目是生成.exe文件的应用程序,另外两个gtestutil是服务于其他五个项目的,它俩生成的是.lib库文件,来为其他五个项目链接使用。

    下图是这个解决方案文件的物理路径:

    可以看到,每个项目名称对应一个同名文件夹。(Algorithm项目对应CS.cpp文件夹,因为Algorithm这个项目名字是中途修改的。)

    除了7个项目名对应的目录,其他几个文件夹的作用如下表所示:

    文件夹作用
    include 项目中使用到的头文件存放于此
    libs 项目中使用到的库文件存放于此, gtest和util这种库工程的输出文件也存放于此,如各种.lib文件
    intermediate 所有项目的”中间目录”集中存放于此
    output 所有应用程序项目的”输出文件”存放于此,如各种.exe文件
    res 项目中用到的资源文件存放于此,比如.txt, .json等文件
    _build 与VC++项目无关,不需留意。

    下面介绍下我是如何把这7个项目组织起来协同工作,并且做到没有冗余文件。

    其实,组织项目很简单,仅需掌握C++程序构建的本质,关键的两个阶段:编译和链接

    第一步,让项目编译通过

    这一步的目标是:让5个生成.exe的项目编译通过。以其中任意一个为例讲解,其他的与之类似。那么我就以TotalSTL为例吧,

    先保证TotalSTL其内部代码没有语法错误。

    其次,因为代码中使用了gtest和util两个项目中的代码,因此需要确保TotalSTL项目能够搜索到gtest和util的头文件。也就是说把gtest和util的头文件所在目录添加到TotalSTL项目的包含路径里即可。增加项目包含目录的操作在上一篇文章中已经提到,这里不再细说。

    需要注意的一点是,由于gtest和util属于公共使用的库,所以最好是把它们的头文件放在一个公共的路径下,比如放在常见的以include命名的目录。这正是前面表格中提到的include文件夹的作用,其物理结构如下图所示:

    可以看到,在include目录下包含了gtest和util等子目录,他们是按照项目来分类,除了gtest和util这两个项目,还有其他的包含文件也集中放在此处。

    我要做的是把include添加到TotalSTL包含目录中,运用上一篇文章学到的环境变量$(SolutionDir),我可以这样编写这个包含目录:

    $(SolutionDir)include/

    添加完包含目录,在TotalSTL项目的main.cpp中,就可以这样引用gtest和util的头文件:

    main.cpp

    #include "gtest/gtest.h"            // gtest是include文件夹的子文件夹,gtest.h是在gtest文件夹下,因此要加上gtest/前缀
    #include "util/FileReader.h"        // 同理,util是include的子文件夹,FileReader.h是在util文件夹下,因此加上util/前缀
    
    void dummyExitFunction() 
    {
        elloop::FileReader::getInstance()->purege();
        char c = getchar();
    }
    
    int main(int argc, char** argv) {
    
    #if defined(_MSC_VER) && defined(_DEBUG)
        // make program stop when debug.
        atexit(dummyExitFunction);
        turnOnMemroyCheck();
    #endif
    
        testing::InitGoogleTest(&argc, argv);
        return RUN_ALL_TESTS();
    }

    其中,#include "gtest/gtest.h", gtest是include文件夹的子文件夹,gtest.h是在gtest文件夹下,因此要加上gtest/前缀
    同理,#include "util/FileReader.h", util是include的子文件夹,FileReader.h是在util文件夹下,因此加上util/前缀

    包含目录配置完毕,项目就能够顺利通过编译了。其他四个项目的配置与TotalSTL的配置一样,也把include加入到包含目录即可。

    第二步:让项目链接通过

    在配置完包含目录,编译通过之后,我如果点“生成”项目,在链接阶段会报错的,是因为五个.exe项目在链接时,没有找到它们依赖的gtest和util库文件。

    这一步就是配置库搜索路径:

    思路是,先确定gtest和util两个项目生成的库文件存放在何处,然后把库文件所在路径加入到其他五个项目的库搜索路径即可。

    1. 确定库文件位置

    跟前面讲的把公共头文件统一放在include目录类似,公共的库文件一般是放到名为lib或者library的文件夹下,正如前文的目录结构图所示,我把它们统一放到了$(SolutionDir)libs目录下,如图,gtest.lib和util.lib就是gtest和util两个项目生成的库文件:

    要想做到让gtest和util这俩项目”把蛋下到libs这里”是需要设置的。以gtest项目为例,进行如下的设置:项目属性 - 库管理器 - 常规 - 输出文件 :

    注意到其中对环境变量的使用,$(SolutionDir)libs/ 就是我的目的地,$(TargetFileName) == gtest.lib

    2. 把库文件所在目录加入到库搜索路径

    现在确定了库文件路径为:

    $(SolutionDir)libs/

    下面把它加入到项目的库搜索路径,还是以TotalSTL项目为例,进行如下操作:项目属性 - 配置属性 - 链接器 - 常规 - 附加库目录

    经过这两个小步骤,就完成了库文件搜索路径的设置。其他的4个项目也按照TotalSTL这样设置一下库搜索目录也就完成了第二步,至此即可保证项目链接通过了。

    调整项目生成顺序

    在设置完文件包含目录和库文件搜索目录之后,当我点击“生成解决方案”的时候,还是可能发生有些项目生成失败的情况。在生成失败之后,我什么也不改,再点一次“生成解决方案”,第二次就生成成功了。这是为什么呢?

    这是因为项目生成顺序问题造成的。我们知道,5个.exe项目依赖gtest和util这俩项目,如果在生成gtest和util之前,就开始生成其他项目,比如TotalSTL, 那么当TotalSTL链接时,发现gtest.lib和util.lib还没有生成,此时就生成失败了。

    而第二次点击生成的时候,此时,gtest和util在第一次生成时已经成功产生gtest.lib和util.lib,第二次生成时,TotalSTL等其他失败的项目重新重试链接,这次找到了两个.lib文件,于是生成成功了。

    怎么能让解决方案一次就生成成功呢?

    这就需要调整项目的生成顺序,很简单,还是以TotalSTL为例,进行如下操作:项目属性 - 通用属性 - 引用 - 添加新引用

    在弹出的列表中,选择其依赖的项目。选择gtest和util,确定即可。

    其他四个.exe项目也做相应处理。设置完毕即可一次生成成功了。

    管理项目的中间目录和输出目录

    在上文的解决方案物理路径图中,还有两个文件夹:intermediate和output 值得介绍一下。

    • intermediate: 项目的中间目录,生成过程中产生的一些中间文件存放于此
    • output: 项目的输出目录,生成的结果文件存放于此,比如TotalSTL.exe, TotalSTL.pdb, TotalSTL.ilk这些类型的文件

    设置这两个目录是为了方便所有项目统一管理,避免混乱。

    下面是这两个目录的设置过程:项目属性 - 配置属性 - 常规 - 输出目录/中间目录

    输出目录的值为: $(SolutionDir)output/$(Configuration)/$(ProjectName)

    中间目录的值为:$(SolutionDir)intermediate/$(Configuration)/$(ProjectName)

    注意其中环境变量的使用:$(SolutionDir)/intermediate 和 $(SolutionDir)output 分别定为到上面提到的两个文件夹,然后按照编译配置, 即$(Configuration)(通常为Debug或者Release)来分目录,最后以项目名称来分目录。

    生成之后的目录结构如下图所示, 可以看到图中路径正是把$(Configuration)(值为Debug), $(ProjectName)(项目名字)代入之后的结果:

    管理可执行文件生成位置

    上面在讲到gtest和util这两个项目的生成.lib的位置时,提到了改变项目的生成文件位置。与之类似,其他5个生成.exe的项目,也可以做设置,使生成的.exe按照统一的目录存放,方便查找和管理。

    以TotalSTL项目为例,具体操作如下:项目属性 - 配置属性 - 链接器 - 常规 - 输出文件

    其值设置为:$(OutDir)$(TargetFileName)

    注意到其中环境变量的使用,其中的$(OutDir)就是上一小节提到的输出目录,其值刚才被设置为$(SolutionDir)output/$(Configuration)/$(ProjectName),把它代入到上面,展开为:

    $(SolutionDir)output/$(Configuration)/$(ProjectName)$(TargetFileName)

    生成之后的物理路径结构为:

    可以看到输出的TotalSTL.exe的路径正是”输出目录”,文件名TotalSTL.exe即$(TargetFileName)

    管理工作目录

    工作目录是程序运行时,搜索资源文件的路径,具体设置在:项目属性 - 配置属性 - 调试 - 工作目录:

    以TrainingGround项目为例:

    其值为:$(SolutionDir)res/, 即对应开篇解决方案图中的res文件夹。

    总结

    本文展示了如何借助Visual Studio的环境变量来组织一个VC++解决方案的工程目录结构。提到了如何使用环境变量来编写头文件包含路径、库文件搜索路径、中间目录、输出目录、输出文件位置、工作目录等。

    解决方案代码地址:CS.cpp  ( https://github.com/elloop/CS.cpp )1.0(visual-studio分支)

    ============= End

  • 相关阅读:
    POJ 2018 二分
    873. Length of Longest Fibonacci Subsequence
    847. Shortest Path Visiting All Nodes
    838. Push Dominoes
    813. Largest Sum of Averages
    801. Minimum Swaps To Make Sequences Increasing
    790. Domino and Tromino Tiling
    764. Largest Plus Sign
    Weekly Contest 128
    746. Min Cost Climbing Stairs
  • 原文地址:https://www.cnblogs.com/lsgxeva/p/12945958.html
Copyright © 2011-2022 走看看