Skip to content

Commit 4e372fe

Browse files
committed
fix: use semicolon separator for LIB env var in Windows cross-compile
1 parent 2a77d02 commit 4e372fe

4 files changed

Lines changed: 143 additions & 11 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vscode-dotnet-deploy",
33
"displayName": "Dotnet 部署工具",
44
"description": "通过 SSH 将 .NET 应用程序部署到 Ubuntu/Linux 服务器",
5-
"version": "0.1.58",
5+
"version": "0.1.61",
66
"publisher": "local",
77
"repository": {
88
"type": "git",

src/crossCompile/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,14 @@
99

1010
export * from './toolchain';
1111
export * from './zigWrapper';
12-
export * from './xwinSetup';
12+
export {
13+
getDefaultXwinSdkPath,
14+
getXwinSdkPath,
15+
downloadWindowsSdk,
16+
getWindowsCrossCompileArgs,
17+
validateWindowsSdk,
18+
getLldLinkPath,
19+
getWindowsCrossCompileEnv,
20+
cleanWindowsSdkCache,
21+
} from './xwinSetup';
1322
export * from './types';

src/crossCompile/xwinSetup.ts

Lines changed: 122 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ export async function getWindowsCrossCompileArgs(runtime: string): Promise<{
137137
args: string[];
138138
env: Record<string, string>;
139139
error?: string;
140+
/** 临时 props 文件路径,需要在构建后删除 */
141+
tempPropsFile?: string;
140142
}> {
141143
const sdkPath = getXwinSdkPath();
142144
const splatPath = path.join(sdkPath, 'splat');
@@ -194,13 +196,9 @@ export async function getWindowsCrossCompileArgs(runtime: string): Promise<{
194196
sdkUcrtPath,
195197
].filter(p => fs.existsSync(p));
196198

197-
// 通过 IlcAdditionalLinkArgs 传递链接器参数
198-
// 使用分号分隔多个参数,避免空格被 MSBuild 误解
199-
const linkerArgs = libPaths.map(p => `/LIBPATH:${p}`).join(';');
200-
args.push(`-p:IlcAdditionalLinkArgs="${linkerArgs}"`);
201-
202-
// 构建 LIB 环境变量 - lld-link 需要这个来查找库
203-
const libEnvPaths = libPaths.join(path.delimiter);
199+
// 设置 LIB 环境变量
200+
// 注意:lld-link 是 Windows 工具,期望使用分号 (;) 分隔路径,而不是 macOS 的冒号 (:)
201+
const libEnvPaths = libPaths.join(';');
204202

205203
// 添加 lld-link 到 PATH
206204
const brewPrefix = process.arch === 'arm64' ? '/opt/homebrew' : '/usr/local';
@@ -217,6 +215,123 @@ export async function getWindowsCrossCompileArgs(runtime: string): Promise<{
217215
return { success: true, args, env };
218216
}
219217

218+
/**
219+
* 临时 props 文件管理
220+
*/
221+
export interface TempPropsFileResult {
222+
/** 创建的文件路径 */
223+
propsFilePath: string;
224+
/** 原始文件的备份路径(如果需要备份) */
225+
backupFilePath?: string;
226+
/** 是否需要恢复原始文件 */
227+
needsRestore: boolean;
228+
}
229+
230+
/**
231+
* 创建临时的 Directory.Build.props 文件用于设置链接器参数
232+
* @param projectDir 项目目录
233+
* @param runtime 目标运行时
234+
* @returns 创建的文件信息,或 null 如果失败
235+
*/
236+
export function createTempLinkerPropsFile(projectDir: string, runtime: string): TempPropsFileResult | null {
237+
const sdkPath = getXwinSdkPath();
238+
const splatPath = path.join(sdkPath, 'splat');
239+
const arch = getWindowsArch(runtime);
240+
241+
const crtLibPath = path.join(splatPath, 'crt', 'lib', arch);
242+
const sdkUmPath = path.join(splatPath, 'sdk', 'lib', 'um', arch);
243+
const sdkUcrtPath = path.join(splatPath, 'sdk', 'lib', 'ucrt', arch);
244+
245+
const libPaths = [crtLibPath, sdkUmPath, sdkUcrtPath].filter(p => fs.existsSync(p));
246+
247+
if (libPaths.length === 0) {
248+
return null;
249+
}
250+
251+
// 构建 LinkerArg ItemGroup
252+
const linkerArgs = libPaths.map(p => ` <LinkerArg Include="/LIBPATH:${p}" />`).join('\n');
253+
254+
// 必须使用 Directory.Build.props 这个名称,MSBuild 才会自动导入
255+
const propsFileName = 'Directory.Build.props';
256+
const propsFilePath = path.join(projectDir, propsFileName);
257+
const backupFilePath = path.join(projectDir, 'Directory.Build.props.dotnet-deploy-backup');
258+
259+
let needsRestore = false;
260+
let originalContent: string | null = null;
261+
262+
// 检查是否已存在 Directory.Build.props
263+
if (fs.existsSync(propsFilePath)) {
264+
// 备份现有文件
265+
try {
266+
originalContent = fs.readFileSync(propsFilePath, 'utf8');
267+
fs.writeFileSync(backupFilePath, originalContent, 'utf8');
268+
needsRestore = true;
269+
} catch {
270+
// 如果无法备份,不继续
271+
return null;
272+
}
273+
274+
// 尝试在现有文件中添加我们的 ItemGroup
275+
// 查找 </Project> 结束标签并在之前插入
276+
const insertIndex = originalContent.lastIndexOf('</Project>');
277+
if (insertIndex !== -1) {
278+
const newContent = originalContent.slice(0, insertIndex) +
279+
` <!-- Auto-generated by vscode-dotnet-deploy for cross-compilation -->
280+
<ItemGroup Condition="'$(PublishAot)' == 'true' and '$(RuntimeIdentifier)' == '${runtime}'">
281+
${linkerArgs}
282+
</ItemGroup>
283+
` + originalContent.slice(insertIndex);
284+
try {
285+
fs.writeFileSync(propsFilePath, newContent, 'utf8');
286+
return { propsFilePath, backupFilePath, needsRestore };
287+
} catch {
288+
// 恢复原始文件
289+
if (needsRestore) {
290+
fs.writeFileSync(propsFilePath, originalContent, 'utf8');
291+
}
292+
return null;
293+
}
294+
}
295+
}
296+
297+
// 创建新的 Directory.Build.props 文件
298+
const propsContent = `<?xml version="1.0" encoding="utf-8"?>
299+
<!-- Auto-generated by vscode-dotnet-deploy for cross-compilation -->
300+
<Project>
301+
<ItemGroup Condition="'$(PublishAot)' == 'true' and '$(RuntimeIdentifier)' == '${runtime}'">
302+
${linkerArgs}
303+
</ItemGroup>
304+
</Project>
305+
`;
306+
307+
try {
308+
fs.writeFileSync(propsFilePath, propsContent, 'utf8');
309+
return { propsFilePath, backupFilePath: needsRestore ? backupFilePath : undefined, needsRestore };
310+
} catch {
311+
return null;
312+
}
313+
}
314+
315+
/**
316+
* 删除或恢复临时 props 文件
317+
*/
318+
export function removeTempLinkerPropsFile(result: TempPropsFileResult): void {
319+
try {
320+
if (result.needsRestore && result.backupFilePath && fs.existsSync(result.backupFilePath)) {
321+
// 恢复原始文件
322+
const originalContent = fs.readFileSync(result.backupFilePath, 'utf8');
323+
fs.writeFileSync(result.propsFilePath, originalContent, 'utf8');
324+
// 删除备份
325+
fs.unlinkSync(result.backupFilePath);
326+
} else if (fs.existsSync(result.propsFilePath)) {
327+
// 删除创建的文件
328+
fs.unlinkSync(result.propsFilePath);
329+
}
330+
} catch {
331+
// 忽略错误
332+
}
333+
}
334+
220335
/**
221336
* 验证 Windows SDK 完整性
222337
*/

src/publisher.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export async function publish(
8181
}
8282

8383
// 获取交叉编译参数
84-
const crossCompileResult = await prepareCrossCompileArgs(options.runtime, options.stripSymbols || false, outputChannel);
84+
const crossCompileResult = await prepareCrossCompileArgs(options.runtime, options.stripSymbols || false, outputChannel, projectDir);
8585

8686
if (crossCompileResult.success) {
8787
args.push(...crossCompileResult.args);
@@ -204,7 +204,8 @@ export async function publish(
204204
async function prepareCrossCompileArgs(
205205
runtime: string,
206206
stripSymbols: boolean,
207-
outputChannel: vscode.OutputChannel
207+
outputChannel: vscode.OutputChannel,
208+
projectDir: string
208209
): Promise<{ success: boolean; args: string[]; env?: Record<string, string>; error?: string }> {
209210
const target = getCrossCompileTarget(runtime);
210211

@@ -229,6 +230,13 @@ async function prepareCrossCompileArgs(
229230
// 合并环境变量
230231
const baseEnv = getWindowsCrossCompileEnv();
231232
const combinedEnv = { ...baseEnv, ...result.env };
233+
234+
outputChannel.appendLine(`[Publisher] LIB env: ${combinedEnv['LIB'] || 'not set'}`);
235+
236+
// 先尝试只使用环境变量,不创建临时文件
237+
// lld-link 应该能读取 LIB 环境变量(使用分号分隔)
238+
// 如果失败,用户可以手动创建 Directory.Build.props 文件
239+
232240
return { success: true, args: result.args, env: combinedEnv };
233241
} else {
234242
return { success: false, args: [], error: result.error };

0 commit comments

Comments
 (0)