|
1 | 1 | import sys
|
2 | 2 | import os
|
3 | 3 | import subprocess
|
| 4 | +import json |
| 5 | +import shutil |
4 | 6 |
|
5 | 7 | def run_cmd(cmd, msg="Failed to run command"):
|
6 | 8 | print('Running ' + ' '.join(cmd))
|
@@ -49,3 +51,204 @@ def remove_files(path, ext):
|
49 | 51 | for file in os.listdir(path):
|
50 | 52 | if file.endswith(ext):
|
51 | 53 | os.remove(os.path.join(path, file))
|
| 54 | + |
| 55 | +def write_csproj_prefix(ioWrapper): |
| 56 | + ioWrapper.write('<Project Sdk="Microsoft.NET.Sdk">\n') |
| 57 | + ioWrapper.write(' <PropertyGroup>\n') |
| 58 | + ioWrapper.write(' <TargetFramework>net8.0</TargetFramework>\n') |
| 59 | + ioWrapper.write(' <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n') |
| 60 | + ioWrapper.write(' <OutputPath>bin\</OutputPath>\n') |
| 61 | + ioWrapper.write( |
| 62 | + ' <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n') |
| 63 | + ioWrapper.write(' </PropertyGroup>\n\n') |
| 64 | + |
| 65 | +class Generator: |
| 66 | + def __init__(self, thisScript, relativeWorkDir, template): |
| 67 | + # /input contains a dotnet project that's being extracted |
| 68 | + self.workDir = os.path.abspath(relativeWorkDir) |
| 69 | + os.makedirs(self.workDir) |
| 70 | + self.thisDir = os.path.abspath(os.path.dirname(thisScript)) |
| 71 | + self.projectNameIn = "input" |
| 72 | + self.projectDirIn = os.path.join(self.workDir, self.projectNameIn) |
| 73 | + self.template = template |
| 74 | + print("\n* Creating new input project") |
| 75 | + self.run_cmd(['dotnet', 'new', self.template, "-f", "net8.0", "--language", "C#", '--name', |
| 76 | + self.projectNameIn, '--output', self.projectDirIn]) |
| 77 | + remove_files(self.projectDirIn, '.cs') |
| 78 | + |
| 79 | + def run_cmd(self, cmd, msg="Failed to run command"): |
| 80 | + run_cmd_cwd(cmd, self.workDir, msg) |
| 81 | + |
| 82 | + def add_nuget(self, nuget, version="latest"): |
| 83 | + print("\n* Adding reference to package: " + nuget) |
| 84 | + cmd = ['dotnet', 'add', self.projectDirIn, 'package', nuget] |
| 85 | + if (version != "latest"): |
| 86 | + cmd.append('--version') |
| 87 | + cmd.append(version) |
| 88 | + self.run_cmd(cmd) |
| 89 | + |
| 90 | + def make_stubs(self): |
| 91 | + # /output contains the output of the stub generation |
| 92 | + outputDirName = "output" |
| 93 | + outputDir = os.path.join(self.workDir, outputDirName) |
| 94 | + |
| 95 | + # /output/raw contains the bqrs result from the query, the json equivalent |
| 96 | + rawOutputDirName = "raw" |
| 97 | + rawOutputDir = os.path.join(outputDir, rawOutputDirName) |
| 98 | + os.makedirs(rawOutputDir) |
| 99 | + |
| 100 | + # /output/output contains a dotnet project with the generated stubs |
| 101 | + projectNameOut = "output" |
| 102 | + projectDirOut = os.path.join(outputDir, projectNameOut) |
| 103 | + |
| 104 | + # /db contains the extracted QL DB |
| 105 | + dbName = 'db' |
| 106 | + dbDir = os.path.join(self.workDir, dbName) |
| 107 | + outputName = "stub" |
| 108 | + outputFile = os.path.join(projectDirOut, outputName + '.cs') |
| 109 | + bqrsFile = os.path.join(rawOutputDir, outputName + '.bqrs') |
| 110 | + jsonFile = os.path.join(rawOutputDir, outputName + '.json') |
| 111 | + |
| 112 | + sdk_version = '8.0.100' |
| 113 | + print("\n* Creating new global.json file and setting SDK to " + sdk_version) |
| 114 | + self.run_cmd(['dotnet', 'new', 'globaljson', '--force', '--sdk-version', sdk_version, '--output', self.workDir]) |
| 115 | + |
| 116 | + print("\n* Running stub generator") |
| 117 | + run_cmd_cwd(['dotnet', 'run', '--project', self.thisDir + '/../../extractor/Semmle.Extraction.CSharp.DependencyStubGenerator/Semmle.Extraction.CSharp.DependencyStubGenerator.csproj'], self.projectDirIn) |
| 118 | + |
| 119 | + print("\n* Creating new raw output project") |
| 120 | + rawSrcOutputDirName = 'src' |
| 121 | + rawSrcOutputDir = os.path.join(rawOutputDir, rawSrcOutputDirName) |
| 122 | + self.run_cmd(['dotnet', 'new', self.template, "--language", "C#", |
| 123 | + '--name', rawSrcOutputDirName, '--output', rawSrcOutputDir]) |
| 124 | + remove_files(rawSrcOutputDir, '.cs') |
| 125 | + |
| 126 | + # copy each file from projectDirIn to rawSrcOutputDir |
| 127 | + pathInfos = {} |
| 128 | + codeqlStubsDir = os.path.join(self.projectDirIn, 'codeql_csharp_stubs') |
| 129 | + for root, dirs, files in os.walk(codeqlStubsDir): |
| 130 | + for file in files: |
| 131 | + if file.endswith('.cs'): |
| 132 | + path = os.path.join(root, file) |
| 133 | + relPath, _ = os.path.splitext(os.path.relpath(path, codeqlStubsDir)) |
| 134 | + origDllPath = "/" + relPath + ".dll" |
| 135 | + pathInfos[origDllPath] = os.path.join(rawSrcOutputDir, file) |
| 136 | + shutil.copy2(path, rawSrcOutputDir) |
| 137 | + |
| 138 | + print("\n --> Generated stub files: " + rawSrcOutputDir) |
| 139 | + |
| 140 | + print("\n* Formatting files") |
| 141 | + self.run_cmd(['dotnet', 'format', 'whitespace', rawSrcOutputDir]) |
| 142 | + |
| 143 | + print("\n --> Generated (formatted) stub files: " + rawSrcOutputDir) |
| 144 | + |
| 145 | + print("\n* Processing project.assets.json to generate folder structure") |
| 146 | + stubsDirName = 'stubs' |
| 147 | + stubsDir = os.path.join(outputDir, stubsDirName) |
| 148 | + os.makedirs(stubsDir) |
| 149 | + |
| 150 | + frameworksDirName = '_frameworks' |
| 151 | + frameworksDir = os.path.join(stubsDir, frameworksDirName) |
| 152 | + |
| 153 | + frameworks = set() |
| 154 | + copiedFiles = set() |
| 155 | + |
| 156 | + assetsJsonFile = os.path.join(self.projectDirIn, 'obj', 'project.assets.json') |
| 157 | + with open(assetsJsonFile) as json_data: |
| 158 | + data = json.load(json_data) |
| 159 | + if len(data['targets']) > 1: |
| 160 | + print("ERROR: More than one target found in " + assetsJsonFile) |
| 161 | + exit(1) |
| 162 | + target = list(data['targets'].keys())[0] |
| 163 | + print("Found target: " + target) |
| 164 | + for package in data['targets'][target].keys(): |
| 165 | + parts = package.split('/') |
| 166 | + name = parts[0] |
| 167 | + version = parts[1] |
| 168 | + packageDir = os.path.join(stubsDir, name, version) |
| 169 | + if not os.path.exists(packageDir): |
| 170 | + os.makedirs(packageDir) |
| 171 | + print(' * Processing package: ' + name + '/' + version) |
| 172 | + with open(os.path.join(packageDir, name + '.csproj'), 'a') as pf: |
| 173 | + |
| 174 | + write_csproj_prefix(pf) |
| 175 | + pf.write(' <ItemGroup>\n') |
| 176 | + |
| 177 | + dlls = set() |
| 178 | + if 'compile' in data['targets'][target][package]: |
| 179 | + for dll in data['targets'][target][package]['compile']: |
| 180 | + dlls.add( |
| 181 | + (name + '/' + version + '/' + dll).lower()) |
| 182 | + if 'runtime' in data['targets'][target][package]: |
| 183 | + for dll in data['targets'][target][package]['runtime']: |
| 184 | + dlls.add((name + '/' + version + '/' + dll).lower()) |
| 185 | + |
| 186 | + for pathInfo in pathInfos: |
| 187 | + for dll in dlls: |
| 188 | + if pathInfo.lower().endswith(dll): |
| 189 | + copiedFiles.add(pathInfo) |
| 190 | + shutil.copy2(pathInfos[pathInfo], packageDir) |
| 191 | + |
| 192 | + if 'dependencies' in data['targets'][target][package]: |
| 193 | + for dependency in data['targets'][target][package]['dependencies'].keys(): |
| 194 | + depVersion = data['targets'][target][package]['dependencies'][dependency] |
| 195 | + pf.write(' <ProjectReference Include="../../' + |
| 196 | + dependency + '/' + depVersion + '/' + dependency + '.csproj" />\n') |
| 197 | + |
| 198 | + if 'frameworkReferences' in data['targets'][target][package]: |
| 199 | + if not os.path.exists(frameworksDir): |
| 200 | + os.makedirs(frameworksDir) |
| 201 | + for framework in data['targets'][target][package]['frameworkReferences']: |
| 202 | + frameworks.add(framework) |
| 203 | + frameworkDir = os.path.join( |
| 204 | + frameworksDir, framework) |
| 205 | + if not os.path.exists(frameworkDir): |
| 206 | + os.makedirs(frameworkDir) |
| 207 | + pf.write(' <ProjectReference Include="../../' + |
| 208 | + frameworksDirName + '/' + framework + '/' + framework + '.csproj" />\n') |
| 209 | + |
| 210 | + pf.write(' <ProjectReference Include="../../' + |
| 211 | + frameworksDirName + '/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj" />\n') |
| 212 | + |
| 213 | + pf.write(' </ItemGroup>\n') |
| 214 | + pf.write('</Project>\n') |
| 215 | + |
| 216 | + # Processing references frameworks |
| 217 | + for framework in frameworks: |
| 218 | + with open(os.path.join(frameworksDir, framework, framework + '.csproj'), 'a') as pf: |
| 219 | + |
| 220 | + write_csproj_prefix(pf) |
| 221 | + pf.write(' <ItemGroup>\n') |
| 222 | + pf.write( |
| 223 | + ' <ProjectReference Include="../Microsoft.NETCore.App/Microsoft.NETCore.App.csproj" />\n') |
| 224 | + pf.write(' </ItemGroup>\n') |
| 225 | + pf.write('</Project>\n') |
| 226 | + |
| 227 | + for pathInfo in pathInfos: |
| 228 | + if framework.lower() + '.ref' in pathInfo.lower(): |
| 229 | + copiedFiles.add(pathInfo) |
| 230 | + shutil.copy2(pathInfos[pathInfo], os.path.join( |
| 231 | + frameworksDir, framework)) |
| 232 | + |
| 233 | + # Processing assemblies in Microsoft.NETCore.App.Ref |
| 234 | + frameworkDir = os.path.join(frameworksDir, 'Microsoft.NETCore.App') |
| 235 | + if not os.path.exists(frameworkDir): |
| 236 | + os.makedirs(frameworkDir) |
| 237 | + with open(os.path.join(frameworksDir, 'Microsoft.NETCore.App', 'Microsoft.NETCore.App.csproj'), 'a') as pf: |
| 238 | + write_csproj_prefix(pf) |
| 239 | + pf.write('</Project>\n') |
| 240 | + |
| 241 | + for pathInfo in pathInfos: |
| 242 | + if 'microsoft.netcore.app.ref/' in pathInfo.lower(): |
| 243 | + copiedFiles.add(pathInfo) |
| 244 | + shutil.copy2(pathInfos[pathInfo], frameworkDir) |
| 245 | + |
| 246 | + for pathInfo in pathInfos: |
| 247 | + if pathInfo not in copiedFiles: |
| 248 | + print('Not copied to nuget or framework folder: ' + pathInfo) |
| 249 | + othersDir = os.path.join(stubsDir, 'others') |
| 250 | + if not os.path.exists(othersDir): |
| 251 | + os.makedirs(othersDir) |
| 252 | + shutil.copy2(pathInfos[pathInfo], othersDir) |
| 253 | + |
| 254 | + print("\n --> Generated structured stub files: " + stubsDir) |
0 commit comments