-
Notifications
You must be signed in to change notification settings - Fork 129
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Implement cpp properties file generator * Run configure and cleanup scripts * Fix linux default c std * Fix typo * Seperate LinkingTo extraction to R file * Clarify extractLinkingTo.R * CamelCase cppProperties folder * Add more checks * Transfer linking to paths URL encoded * Minor trim * Rename generate c/cpp config command Co-authored-by: Kun Ren <renkun@outlook.com>
- Loading branch information
1 parent
e3ede0d
commit ad20fdc
Showing
4 changed files
with
235 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
deps <- read.dcf("DESCRIPTION", "LinkingTo") | ||
if (length(deps) == 0) { # Empty file | ||
deps <- "" | ||
} else { | ||
deps <- unname(deps[1, ]) # Read 'LinkingTo' field from description | ||
deps <- gsub("\\s|\\n|(\\([^\\)]*\\))", "", deps) # Remove all whitespace, line breaks and version constraints | ||
deps <- strsplit(deps, ",")[[1]] # Split package names | ||
deps <- vapply(deps, function(pkg) { | ||
system.file("include", package = pkg) | ||
}, character(1)) # Lookup include dir | ||
deps <- utils::URLencode(deps) | ||
} | ||
writeLines(deps) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
'use strict'; | ||
|
||
import { randomBytes } from 'crypto'; | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import { window } from 'vscode'; | ||
import { getRpath, getCurrentWorkspaceFolder, executeRCommand } from './util'; | ||
import { execSync } from 'child_process'; | ||
import { extensionContext } from './extension'; | ||
|
||
export async function generateCppProperties(): Promise<void> { | ||
const currentWorkspaceFolder = getCurrentWorkspaceFolder()?.uri.fsPath; | ||
if (currentWorkspaceFolder === undefined) { | ||
void window.showWarningMessage('Please open a workspace folder to create c_cpp_properties.json'); | ||
return; | ||
} | ||
const outFilePath = path.join(currentWorkspaceFolder, '.vscode', 'c_cpp_properties.json'); | ||
if (fs.existsSync(outFilePath)) { | ||
const overwrite = await window.showWarningMessage( | ||
'"c_cpp_properties.json" file already exists. Do you want to overwrite?', | ||
'Yes', 'No' | ||
); | ||
if (overwrite === 'No') { | ||
return; | ||
} | ||
void fs.unlinkSync(outFilePath); | ||
} | ||
return generateCppPropertiesProc(currentWorkspaceFolder); | ||
} | ||
|
||
/** Helper: Return object depending on current process platform */ | ||
function platformChoose<A, B, C>(win32: A, darwin: B, other: C): A | B | C { | ||
return process.platform === 'win32' ? win32 : | ||
process.platform === 'darwin' ? darwin : | ||
other; | ||
} | ||
|
||
// See: https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference | ||
async function generateCppPropertiesProc(workspaceFolder: string) { | ||
const rPath = await getRpath(); | ||
|
||
// Collect information from running the compiler | ||
const configureFile = platformChoose('configure.win', 'configure', 'configure'); | ||
const cleanupFile = platformChoose('cleanup.win', 'cleanup', 'cleanup'); | ||
|
||
if (fs.existsSync(path.join(workspaceFolder, configureFile))) { | ||
await executeRCommand(`system("sh ./${configureFile}")`, workspaceFolder, (e: Error) => { | ||
void window.showErrorMessage(e.message); | ||
return ''; | ||
}); | ||
} | ||
|
||
const compileOutputCpp = collectCompilerOutput(rPath, workspaceFolder, 'cpp'); | ||
const compileOutputC = collectCompilerOutput(rPath, workspaceFolder, 'c'); | ||
|
||
if (fs.existsSync(path.join(workspaceFolder, cleanupFile))) { | ||
await executeRCommand(`system("sh ./${cleanupFile}")`, workspaceFolder, (e: Error) => { | ||
void window.showErrorMessage(e.message); | ||
return ''; | ||
}); | ||
} | ||
|
||
const compileInfo = extractCompilerInfo(compileOutputCpp); | ||
const compileStdCpp = extractCompilerStd(compileOutputCpp); | ||
const compileStdC = extractCompilerStd(compileOutputC); | ||
const compileCall = extractCompilerCall(compileOutputCpp); | ||
const compilerPath = await executeRCommand(`cat(Sys.which("${compileCall}"))`, workspaceFolder, (e: Error) => { | ||
void window.showErrorMessage(e.message); | ||
return ''; | ||
}); | ||
|
||
const intelliSensePlatform = platformChoose('windows', 'macos', 'linux'); | ||
const intelliSenseComp = compileCall ? (compileCall.includes('clang') ? 'clang' : 'gcc') : 'gcc'; | ||
const intelliSense = `${intelliSensePlatform}-${intelliSenseComp}-${process.arch}`; | ||
|
||
// Collect information from 'DESCRIPTION' | ||
const linkingToIncludes = await collectRLinkingTo(workspaceFolder); | ||
|
||
// Combine information | ||
const envIncludes: string[] = ['${workspaceFolder}/src']; | ||
envIncludes.push(...compileInfo.compIncludes.map((v) => path.isAbsolute(v) ? v : `\${workspaceFolder}/${path.join('src', v)}`)); | ||
envIncludes.push(...linkingToIncludes); | ||
|
||
const envDefines = compileInfo.compDefines; | ||
|
||
// If no standard is set on linux, the C standard seems to default to the c++ one. | ||
const envCStd = (!compileStdC || compileStdC.includes('++')) ? '${default}' : compileStdC; | ||
|
||
const platformName = platformChoose('Win32', 'Mac', 'Linux'); | ||
|
||
// Build json | ||
const re = { | ||
'configurations': [{ | ||
'name': platformName, | ||
'defines': envDefines, | ||
'includePath': envIncludes, | ||
'compilerPath': compilerPath, | ||
'cStandard': envCStd, | ||
'cppStandard': compileStdCpp, | ||
'intelliSenseMode': intelliSense | ||
}], | ||
'version': 4 | ||
}; | ||
const ser = JSON.stringify(re, null, 2); | ||
|
||
// Write file | ||
const vscodeDir = path.join(workspaceFolder, '.vscode'); | ||
if (!fs.existsSync(vscodeDir)) { | ||
fs.mkdirSync(vscodeDir); | ||
} | ||
fs.writeFileSync(path.join(vscodeDir, 'c_cpp_properties.json'), ser); | ||
} | ||
|
||
async function collectRLinkingTo(workspaceFolder: string): Promise<string[]> { | ||
if (!fs.existsSync(path.join(workspaceFolder, 'DESCRIPTION'))) { | ||
return []; | ||
} | ||
|
||
const rScript = extensionContext.asAbsolutePath('R/cppProperties/extractLinkingTo.R').replace(/\\/g, '/'); | ||
const linkingToIncludesStr = (await executeRCommand(`source('${rScript}')`, workspaceFolder, (e: Error) => { | ||
void window.showErrorMessage(e.message); | ||
return ''; | ||
}))?.trim(); | ||
if (!linkingToIncludesStr || linkingToIncludesStr === '') { | ||
return []; | ||
} | ||
return linkingToIncludesStr.split(/\r?\n/g).map(decodeURI); | ||
} | ||
|
||
function ensureUnquoted(str: string): string { | ||
if (/(^".*"$)|(^'.*'$)/.test(str)) { | ||
return str.substring(1, str.length - 1); | ||
} | ||
return str; | ||
} | ||
|
||
function extractCompilerInfo(compileOutput: string) { | ||
const rxCompArg = /-(I|D)("[^"]+"|[\S]+)/gm; | ||
|
||
const compDefines: string[] = []; | ||
const compIncludes: string[] = []; | ||
const compLookup = { 'D': compDefines, 'I': compIncludes }; | ||
|
||
let m: RegExpExecArray | null; | ||
while ((m = rxCompArg.exec(compileOutput)) !== null) { | ||
if (m.index === rxCompArg.lastIndex) { | ||
rxCompArg.lastIndex++; | ||
} | ||
|
||
// The regex guarantees that the first group is 'I' or 'D' | ||
compLookup[(m[1] as 'D' | 'I')].push(ensureUnquoted(m[2])); | ||
} | ||
|
||
return { | ||
compDefines: compDefines, | ||
compIncludes: compIncludes | ||
}; | ||
} | ||
|
||
function extractCompilerStd(compileOutput: string): string | undefined { | ||
const rxStd = /-std=(\S+)/; | ||
|
||
const stdMatch = compileOutput.match(rxStd); | ||
return stdMatch?.[1]; | ||
} | ||
|
||
function extractCompilerCall(compileOutput: string): string | undefined { | ||
const rxComp = /("[^"]+"|[\S]+)/; | ||
const ccalls = compileOutput.split('\n'); | ||
if (ccalls.length < 2) { | ||
return undefined; | ||
} | ||
|
||
const m = ccalls[1].match(rxComp); | ||
return m?.[1]; | ||
} | ||
|
||
function createTempDir(root: string): string { | ||
let tempDir: string; | ||
while (fs.existsSync(tempDir = path.join(root, `___temp_${randomBytes(8).toString('hex')}`))) { /* Name clash */ } | ||
fs.mkdirSync(tempDir); | ||
return tempDir; | ||
} | ||
|
||
function collectCompilerOutput(rPath: string, workspaceFolder: string, testExtension: 'cpp' | 'c') { | ||
|
||
const makevarsFiles = ['Makevars', 'Makevars.win', 'Makevars.ucrt']; | ||
|
||
const srcFolder = path.join(workspaceFolder, 'src'); | ||
const tempFolder = createTempDir(workspaceFolder); | ||
|
||
// Copy makevars | ||
if (fs.existsSync(srcFolder)) { | ||
const projectMakevarsFiles = fs.readdirSync(srcFolder).filter(fn => makevarsFiles.includes(fn)); | ||
for (const f of projectMakevarsFiles) { | ||
fs.copyFileSync(path.join(srcFolder, f), path.join(tempFolder, f)); | ||
} | ||
} | ||
|
||
// Create dummy source file | ||
const testFile = `comp_test.${testExtension}`; | ||
fs.writeFileSync(path.join(tempFolder, testFile), ''); | ||
|
||
// Compile dummy | ||
const command = `"${rPath}" CMD SHLIB ${testFile}`; | ||
const compileOutput = execSync(command, { | ||
cwd: tempFolder | ||
}).toString(); | ||
|
||
// Cleanup | ||
fs.rmSync(tempFolder, { recursive: true, force: true }); | ||
|
||
return compileOutput; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters