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
|
--!A cross-platform build utility based on Lua
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
-- @author OpportunityLiu
-- @file vsxmake.lua
--
-- imports
import("core.base.option")
import("core.base.hashset")
import("vstudio.impl.vsinfo", { rootdir = path.directory(os.scriptdir()) })
import("render")
import("getinfo")
import("core.project.config")
import("core.cache.localcache")
import("vstudio.impl.vsutils", {rootdir = path.join(os.programdir(), "plugins", "project")})
local template_root = path.join(os.programdir(), "scripts", "vsxmake", "vsproj", "templates")
local template_sln = path.join(template_root, "sln", "vsxmake.sln")
local template_vcx = path.join(template_root, "vcxproj", "#target#.vcxproj")
local template_fil = path.join(template_root, "vcxproj.filters", "#target#.vcxproj.filters")
local template_props = path.join(template_root, "Xmake.Custom.props")
local template_targets = path.join(template_root, "Xmake.Custom.targets")
local template_items = path.join(template_root, "Xmake.Custom.items")
local template_itemfil = path.join(template_root, "Xmake.Custom.items.filters")
function _filter_files(files, includeexts, excludeexts)
local positive = not excludeexts
local extset = hashset.from(positive and includeexts or excludeexts)
local f = {}
for _, file in ipairs(files) do
local ext = path.extension(file)
if (positive and extset:has(ext)) or not (positive or extset:has(ext)) then
table.insert(f, file)
end
end
table.sort(f)
return f
end
function _buildparams(info, target, default)
local function getprop(match, opt)
local i = info
local r = info[match]
if target then
opt = table.join(target, opt)
end
for _, k in ipairs(opt) do
local v = (i._targets or {})[k]
if v == nil and i._arch_modes then
v = i._arch_modes[k]
end
if v == nil and i._paths then
v = i._paths[k]
end
if v == nil and i._dirs then
v = i._dirs[k]
end
if v == nil and i._deps then
v = i._deps[k]
end
if v == nil and i._groups then
v = i._groups[k]
end
if v == nil and i._group_deps then
v = i._group_deps[k]
end
if v == nil then
v = i[k]
end
if v == nil then
raise("key '" .. k .. "' not found")
end
i = v
r = i[match] or r
end
return r or default
end
local function listconfig(args)
for _, k in ipairs(args) do
args[k] = true
end
local r = {}
if args.target then
table.insert(r, info.targets)
end
if args.mode then
table.insert(r, info.modes)
end
if args.arch then
table.insert(r, info.archs)
end
if args.group then
table.insert(r, info.groups)
end
if args.group_dep then
table.insert(r, info.group_deps)
end
if args.dir then
table.insert(r, info._targets[target].dirs)
end
if args.dep then
table.insert(r, info._targets[target].deps)
end
if args.filec then
local files = info._targets[target].sourcefiles
table.insert(r, _filter_files(files, {".c"}))
elseif args.filecxx then
local files = info._targets[target].sourcefiles
table.insert(r, _filter_files(files, {".cpp", ".cc", ".cxx"}))
elseif args.filempp then
local files = info._targets[target].sourcefiles
table.insert(r, _filter_files(files, {".mpp", ".mxx", ".cppm", ".ixx"}))
elseif args.filecu then
local files = info._targets[target].sourcefiles
table.insert(r, _filter_files(files, {".cu"}))
elseif args.fileobj then
local files = info._targets[target].sourcefiles
table.insert(r, _filter_files(files, {".obj", ".o"}))
elseif args.filerc then
local files = info._targets[target].sourcefiles
table.insert(r, _filter_files(files, {".rc"}))
elseif args.fileui then -- for qt/.ui
local files = info._targets[target].sourcefiles
table.insert(r, _filter_files(files, {".ui"}))
elseif args.fileqrc then -- for qt/.qrc
local files = info._targets[target].sourcefiles
table.insert(r, _filter_files(files, {".qrc"}))
elseif args.filets then -- for qt/.ts
local files = info._targets[target].sourcefiles
table.insert(r, _filter_files(files, {".ts"}))
elseif args.incc then
local files = table.join(info._targets[target].headerfiles or {}, info._targets[target].extrafiles)
table.insert(r, _filter_files(files, nil, {".natvis"}))
elseif args.incnatvis then
local files = table.join(info._targets[target].headerfiles or {}, info._targets[target].extrafiles)
table.insert(r, _filter_files(files, {".natvis"}))
end
return r
end
return function(match, opt)
if type(match) == "table" then
return listconfig(match)
end
return getprop(match, opt)
end
end
function _trycp(file, target, targetname)
targetname = targetname or path.filename(file)
local targetfile = path.join(target, targetname)
targetfile = vsutils.translate_path(targetfile)
if os.isfile(targetfile) then
dprint("skipped file %s since the file already exists", path.relative(targetfile))
return
end
os.cp(file, targetfile)
end
function _writefileifneeded(file, content)
file = vsutils.translate_path(file)
if os.isfile(file) and io.readfile(file) == content then
dprint("skipped file %s since the file has the same content", path.relative(file))
return
end
-- we need utf8 with bom encoding for unicode
-- @see https://github.com/xmake-io/xmake/issues/1689
io.writefile(file, content, {encoding = "utf8bom"})
end
-- save plugin arguments for `plugin.vsxmake.autoupdate`
-- @see https://github.com/xmake-io/xmake/issues/1895
function _save_plugin_arguments()
local vsxmake_cache = localcache.cache("vsxmake")
for _, name in ipairs({"kind", "modes", "archs", "outputdir"}) do
vsxmake_cache:set(name, option.get(name))
end
vsxmake_cache:save()
end
-- clear cache
function _clear_cache()
localcache.clear("detect")
localcache.clear("option")
localcache.clear("package")
localcache.clear("toolchain")
-- force recheck
localcache.set("config", "recheck", true)
localcache.save()
end
-- make
function make(version)
if not version then
version = tonumber(config.get("vs"))
if not version then
return function(outputdir)
raise("invalid vs version, run `xmake f --vs=20xx`")
end
end
end
return function(outputdir)
vprint("using project kind vs%d", version)
assert(version >= 2010, "vsxmake does not support vs version lower than 2010")
-- get info and params
local info = getinfo(outputdir, vsinfo(version))
local paramsprovidersln = _buildparams(info)
-- write solution file
local sln = path.join(info.solution_dir, info.slnfile .. ".sln")
_writefileifneeded(sln, render(template_sln, "#([A-Za-z0-9_,%.%*%(%)]+)#", "@([^@]+)@", paramsprovidersln))
-- add solution custom file
_trycp(template_props, info.vcxproj_rootdir)
_trycp(template_targets, info.vcxproj_rootdir)
for _, target in ipairs(info.targets) do
local paramsprovidertarget = _buildparams(info, target, "<!-- nil -->")
local vcxproj_dir = info._targets[target].vcxprojdir
-- write project file
local vcxproj_file = path.join(vcxproj_dir, target .. ".vcxproj")
_writefileifneeded(vcxproj_file, render(template_vcx, "#([A-Za-z0-9_,%.%*%(%)]+)#", "@([^@]+)@", paramsprovidertarget))
local vcxproj_filters = path.join(vcxproj_dir, target .. ".vcxproj.filters")
_writefileifneeded(vcxproj_filters, render(template_fil, "#([A-Za-z0-9_,%.%*%(%)]+)#", "@([^@]+)@", paramsprovidertarget))
-- add project custom file
_trycp(template_props, vcxproj_dir)
_trycp(template_targets, vcxproj_dir)
_trycp(template_items, vcxproj_dir)
_trycp(template_itemfil, vcxproj_dir)
end
-- clear config and local cache
_clear_cache()
-- save plugin arguments for autoupdate
_save_plugin_arguments()
end
end
|