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
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>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
3>关于lua配置文件:
3.1>lua配置文件需要配置的字段:
pro_name:字符串
root_namespace:字符串
platform_ver:字符串
project_ver:字符串
sdk_ver:字符串
configuration_type:字符串
charset:字符串
head_files:字符串数组
source_files:字符串数组
compile_configs:自定义结构数组(参考示例)
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")
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 = {} --定义预处理符号
}
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"
}