1.问题或需求描述:

手动创建VS2019 C++工程在某些场景下可能过于繁琐且易出错,例如引用类库可能需要同时配置多个编译模式(debug,realse);可能需要每次手动导入一些基础代码;或许使用一个标准工程副本可以解决问题,但还不够灵活。

2.测试环境:

win10 x64, VS2019 v142

3.解决方法或原理:

3.1方法:

1>将相关库、基础模板等集中打包在一个压缩文件(例:stdlib_tmpl.zip)中。

2>根据使用场景配置相关lua配置文件,然后从lua配置文件快速创建msvc c++工程(自动从模板代码压缩文件'stdlib_tmpl.zip'中抽取所需代码)

3.2方案:

下载链接: https://[百度网盘]/s/1V_5ng4g64OwA5yVX7c_LfA?pwd=cpp0  提取码:cpp0

从配置文件自动创建MSVC C++工程_dll

gen_vcx_proj options:
  -h [ --help ]                       produce help message
  -f [ --config ] arg (=./config.lua) builder config lua
  -m [ --make ]                       make builder config lua template
  -c [ --console ]                    console builder
  -d [ --dll ]                        dll builder
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

从配置文件自动创建MSVC C++工程_lua_02

1>gen_vcx_proj.exe 用法示例:

生成console工程lua配置模板:gen_vcj_proj -mc -f console-cfg.lua

生成dll工程lua配置模板:gen_vcj_proj -md -f dll-cfg.lua

构建console工程:gen_vcj_proj -c -f console-cfg.lua

构建dll工程:gen_vcj_proj -d -f dll-cfg.lua

2>示例 console-cfg.lua:

pro_name = "MyApplication"
root_namespace = "MyApplication"
platform_ver="v142"
project_ver="16.0"
sdk_ver="10.0"
configuration_type = "Application"
charset="Unicode"
head_files = {"framework.h", "utils.h", "udf.h"}
source_files = {"main.cpp", "utils.cpp"}

zip.extract_dir("./stdlib_tmpl.zip", "libs", "./" .. pro_name .. "/libs")
zip.extract_file("./stdlib_tmpl.zip", "console/framework.h", "./" .. pro_name .. "/framework.h")
zip.extract_file("./stdlib_tmpl.zip", "console/udf.h", "./" .. pro_name .. "/udf.h")
zip.extract_file("./stdlib_tmpl.zip", "console/utils.h", "./" .. pro_name .. "/utils.h")
zip.extract_file("./stdlib_tmpl.zip", "console/utils.cpp", "./" .. pro_name .. "/utils.cpp")
zip.extract_file("./stdlib_tmpl.zip", "console/main/cmd_program_option.cpp", "./" .. pro_name .. "/main.cpp")
zip.extract_file("./stdlib_tmpl.zip", ".gitattributes", "./" .. pro_name .. "/.gitattributes")
zip.extract_file("./stdlib_tmpl.zip", ".gitignore", "./" .. pro_name .. "/.gitignore")

function contains(array, element)
    for _, value in ipairs(array) do
        if value == element then
            return true
        end
    end
    return false
end

function array_union(array1, array2)
    local result = {}

    for _, v in ipairs(array1) do
        if not contains(result, v) then
            table.insert(result, v)
        end
    end

    for _, v in ipairs(array2) do
        if not contains(result, v) then
            table.insert(result, v)
        end
    end

    return result
end

function gen_exports_macro(input_str)
    local result = ""
    
    for i = 1, #input_str do
        local char = input_str:sub(i, i)
        if char:match("[%a%d]") then
            result = result .. char:upper()
        end
    end
    
    return result
end

local LibraryConfig = {
    is_use = false,
    compile = '',
    inner_dir = '',
    outer_dir = '',
    inc_dirs = {},
    inc_cpps = {},
    lib_dirs = {},
    depends = {},
    pre_def = {}
}

function LibraryConfig:new(is_use, compile, inner_dir, outer_dir, inc_dirs, inc_cpps, lib_dirs, depends, pre_def)
    local o = {}
    setmetatable(o, self)
    self.__index = self
    o.is_use = is_use or self.is_use
    o.compile = compile or self.compile
    o.inner_dir = inner_dir or self.inner_dir
    o.outer_dir = outer_dir or self.outer_dir
    o.inc_dirs = inc_dirs or self.inc_dirs
    o.inc_cpps = inc_cpps or self.inc_cpps
    o.lib_dirs = lib_dirs or self.lib_dirs
    o.depends = depends or self.depends
    o.pre_def = pre_def or self.pre_def
    return o
end

function LibraryConfig:to_string()
    local str = ''
    str = str .. 'is_use: ' .. tostring(self.is_use) .. '\n'
    str = str .. 'compile: ' .. tostring(self.compile) .. '\n'
    str = str .. 'inner_dir: ' .. tostring(self.inner_dir) .. '\n'
    str = str .. 'outer_dir: ' .. tostring(self.outer_dir) .. '\n'
    str = str .. 'inc_dirs: ' .. table.concat(self.inc_dirs, '') .. '\n'
    str = str .. 'inc_cpps: ' .. table.concat(self.inc_cpps, '') .. '\n'
    str = str .. 'lib_dirs: ' .. table.concat(self.lib_dirs, '') .. '\n'
    str = str .. 'depends: ' .. table.concat(self.depends, ';') .. '\n'
    str = str .. 'pre_def: ' .. table.concat(self.pre_def, ';')
    return str
end

boost_cfg = LibraryConfig:new(
    true, '', nil, nil,
    {'$(BOOST_ROOT)'},
    {},
    {'$(BOOST_LIB)'},
    {},
    {'USE_BOOST'}
    )

easylogging_cfg = LibraryConfig:new(
    true, '', nil, nil,
    {'./libs/easylogging++'},
    {'./libs/easylogging++/easylogging++.cc'},
    {},
    {},
    {'ELPP_THREAD_SAFE', 'USE_EASYLOGGINGPP'}
    )

loguru_cfg = LibraryConfig:new(
    true, '', nil, nil,
    {'./libs/loguru'},
    {'./libs/loguru/loguru.cpp'},
    {},
    {},
    {'USE_LOGURU'}
    )

getopt_cfg = LibraryConfig:new(
    false, '', nil, nil,
    {'./libs/getopt'},
    {},
    {},
    {},
    {'USE_GETOPT'}
    )

opencv2_d_cfg = LibraryConfig:new(
    true, 'debug', nil, nil,
    {'$(opencv2_include)'},
    {},
    {'$(opencv2_lib)'},
    {'$(opencv2_lib_world_d)'},
    {'USE_OPENCV2'}
    )

opencv2_r_cfg = LibraryConfig:new(
    true, 'release', nil, nil,
    {'$(opencv2_include)'},
    {},
    {'$(opencv2_lib)'},
    {'$(opencv2_lib_world)'},
    {'USE_OPENCV2'}
    )

halcon_cfg = LibraryConfig:new(
    true, '', nil, nil,
    {'$(HALCONROOT)/include', '$(HALCONROOT)/include/halconcpp'},
    {},
    {'$(HALCONROOT)/lib/$(HALCONARCH)'},
    {'halconcpp.lib'},
    {'USE_HALCON'}
    )

json_cfg = LibraryConfig:new(
    true, '', nil, nil,
    {'./libs/json-3.11.2'},
    {},
    {},
    {},
    {'USE_JSON'}
    )

sqlite_cfg = LibraryConfig:new(
    false, '', nil, nil,
    {'./libs/sqlite-3420000'},
    {},
    {},
    {},
    {'USE_SQLITE'}
    )

libiconv_cfg = LibraryConfig:new(
    true, '', nil, nil,
    {'./libs/libiconv-1.17/include'},
    {},
    {'./libs/libiconv-1.17/lib'},
    {'libiconv_a.lib'},
    {'USE_LIBICONV'}
    )

lua_cfg = LibraryConfig:new(
    true, '', nil, nil,
    {'./libs/lua-5.4.6/include'},
    {},
    {'./libs/lua-5.4.6/x64/vc16/lib'},
    {'lua5.4.6.lib'},
    {'USE_LUA'}
    )

thread_pool_cfg = LibraryConfig:new(
    false, '', nil, nil,
    {'./libs/ThreadPool'},
    {},
    {},
    {},
    {'USE_THREADPOOL'}
    )

tinyxml2_cfg = LibraryConfig:new(
    false, '', nil, nil,
    {'./libs/tinyxml2'},
    {'./libs/tinyxml2/tinyxml2.cpp'},
    {},
    {},
    {'USE_TINYXML2'}
    )

libs = {}
table.insert (libs, boost_cfg)
table.insert (libs, easylogging_cfg)
table.insert (libs, loguru_cfg)
table.insert (libs, getopt_cfg)
table.insert (libs, opencv2_d_cfg)
table.insert (libs, opencv2_r_cfg)
table.insert (libs, halcon_cfg)
table.insert (libs, json_cfg)
table.insert (libs, sqlite_cfg)
table.insert (libs, libiconv_cfg)
table.insert (libs, lua_cfg)
table.insert (libs, thread_pool_cfg)
table.insert (libs, tinyxml2)

-- debug
--~ for i, v in ipairs(libs) do
--~     print(i,':')
--~     print(v:to_string())
--~ end

for _, v in ipairs(libs) do
    if v.is_use then
        source_files = array_union(source_files, v.inc_cpps)
    end
end

local Itemdefinition = {
    compile = "Debug|x64",
    inc_dirs = {},
    lib_dirs = {},
    depends = {"%(AdditionalDependencies)"},
    pre_def = {"WIN32", "_DEBUG", "_CONSOLE", "ELPP_THREAD_SAFE", "%(PreprocessorDefinitions)"},
    sub_system = "Console",
    warning_level = "Level3"
}

function Itemdefinition:new(compile, inc_dirs, lib_dirs, depends, pre_def, sub_system, warning_level)
    local o = {}
    setmetatable(o, self)
    self.__index = self
    o.compile = compile or self.compile
    o.inc_dirs = inc_dirs or self.inc_dirs
    o.lib_dirs = lib_dirs or self.lib_dirs
    o.depends = depends or self.depends
    o.pre_def = pre_def or self.pre_def
    o.sub_system = sub_system or self.sub_system
    o.warning_level = warning_level or self.warning_level
    return o
end

Debug_Win32 = Itemdefinition:new("Debug|Win32",
    {},
    {},
    {"%(AdditionalDependencies)"},
    {"WIN32", "_DEBUG", "_CONSOLE", "%(PreprocessorDefinitions)"}
    )
Release_Win32 = Itemdefinition:new("Release|Win32",
    {},
    {},
    {"%(AdditionalDependencies)"},
    {"WIN32", "NDEBUG", "_CONSOLE", "%(PreprocessorDefinitions)"}
    )
Debug_x64 = Itemdefinition:new("Debug|x64",
    {},
    {},
    {"%(AdditionalDependencies)"},
    {"_DEBUG", "_CONSOLE", "%(PreprocessorDefinitions)"}
    )
Release_x64 = Itemdefinition:new("Release|x64",
    {},
    {},
    {"%(AdditionalDependencies)"},
    {"NDEBUG", "_CONSOLE", "%(PreprocessorDefinitions)"}
    )

compile_configs = { Debug_Win32, Release_Win32, Debug_x64, Release_x64}
for i, v in ipairs(compile_configs) do
    inc_dirs = {}
    lib_dirs = {}
    depends = {}
    pre_def = {}
    for i2, v2 in ipairs(libs) do
        if v2.is_use then
            if v2.compile ~= nil and string.len(v2.compile) > 0 then
                if string.find(string.lower(v.compile), string.lower(v2.compile)) ~= nil then
                    inc_dirs = array_union(inc_dirs, v2.inc_dirs)
                    lib_dirs = array_union(lib_dirs, v2.lib_dirs)
                    depends  = array_union(depends, v2.depends)
                    pre_def  = array_union(pre_def, v2.pre_def)
                end
            elseif v2.compile == nil or string.len(v2.compile) == 0 then
                inc_dirs = array_union(inc_dirs, v2.inc_dirs)
                lib_dirs = array_union(lib_dirs, v2.lib_dirs)
                depends  = array_union(depends, v2.depends)
                pre_def  = array_union(pre_def, v2.pre_def)
            end
        end
    end
    v.inc_dirs = array_union(inc_dirs, v.inc_dirs)
    v.lib_dirs = array_union(lib_dirs, v.lib_dirs)
    v.depends  = array_union(depends, v.depends)
    v.pre_def  = array_union(pre_def, v.pre_def)
end

--debug
--~ for _, config in ipairs(compile_configs) do
--~     print("compile: " .. config.compile)
--~     print("inc_dirs: " .. table.concat(config.inc_dirs, ";"))
--~     print("lib_dirs: " .. table.concat(config.lib_dirs, ";"))
--~     print("depends: " .. table.concat(config.depends, ";"))
--~     print("pre_def: " .. table.concat(config.pre_def, ";"))
--~     print("sub_system: " .. config.sub_system)
--~     print("warning_level: " .. config.warning_level)
--~     print("------")
--~ end
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
  • 259.
  • 260.
  • 261.
  • 262.
  • 263.
  • 264.
  • 265.
  • 266.
  • 267.
  • 268.
  • 269.
  • 270.
  • 271.
  • 272.
  • 273.
  • 274.
  • 275.
  • 276.
  • 277.
  • 278.
  • 279.
  • 280.
  • 281.
  • 282.
  • 283.
  • 284.
  • 285.
  • 286.
  • 287.
  • 288.
  • 289.
  • 290.
  • 291.
  • 292.
  • 293.
  • 294.
  • 295.
  • 296.
  • 297.
  • 298.
  • 299.
  • 300.
  • 301.
  • 302.
  • 303.
  • 304.
  • 305.
  • 306.
  • 307.
  • 308.
  • 309.
  • 310.
  • 311.
  • 312.
  • 313.
  • 314.
  • 315.
  • 316.
  • 317.
  • 318.
  • 319.
  • 320.
  • 321.
  • 322.
  • 323.
  • 324.
  • 325.
  • 326.
  • 327.
  • 328.
  • 329.
  • 330.
  • 331.
  • 332.
  • 333.
  • 334.

3>关于lua配置文件:

3.1>lua配置文件需要配置的字段:

pro_name:字符串
root_namespace:字符串
platform_ver:字符串
project_ver:字符串
sdk_ver:字符串
configuration_type:字符串
charset:字符串
head_files:字符串数组
source_files:字符串数组 
compile_configs:自定义结构数组(参考示例)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

3.2>lua zip api:

--从zip中抽取目录(zip_file_path, inner_dir_path, outer_dir_path)
zip.extract_dir("./stdlib_tmpl.zip", "libs", "./" .. pro_name .. "/libs")
--从zip中抽取文件(zip_file_path, inner_file_path, outer_file_path)
zip.extract_file("./stdlib_tmpl.zip", "console/framework.h", "./" .. pro_name .. "/framework.h")
  • 1.
  • 2.
  • 3.
  • 4.

3.3> LibraryConfig 结构

local LibraryConfig = {
    is_use = false, --是否启用库
    compile = '',   --编译模式,debug,release,nil(nil或空字符串匹配所有模式)
    inner_dir = '', --库位于zip中的路径(需要在脚本中自行实现)
    outer_dir = '', --需要抽取到的外部目标路径(需要在脚本中自行实现)
    inc_dirs = {}, --附加头文件包含目录
    inc_cpps = {}, --包含源代码(*.cpp)(适用某些库需包含源代码)
    lib_dirs = {}, --库文件目录
    depends = {}, --库文件
    pre_def = {} --定义预处理符号
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

3.4> Itemdefinition 结构

local Itemdefinition = {
    compile = "Debug|x64",
    inc_dirs = {},
    lib_dirs = {},
    depends = {"%(AdditionalDependencies)"},
    pre_def = {"WIN32", "_DEBUG", "_CONSOLE", "ELPP_THREAD_SAFE", "%(PreprocessorDefinitions)"},
    sub_system = "Console",
    warning_level = "Level3"
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

4.结果演示

从配置文件自动创建MSVC C++工程_c++_03

从配置文件自动创建MSVC C++工程_c++_04