|
1 | 1 | import sys |
2 | 2 | import os |
3 | 3 | import helpers |
| 4 | +import json |
| 5 | +import shutil |
4 | 6 |
|
5 | 7 | print('Script to generate stub file from a nuget package') |
6 | 8 | print(' Usage: python ' + sys.argv[0] + |
7 | | - ' NUGET_PACKAGE_NAME [VERSION=latest] [WORK_DIR=tempDir] [OUTPUT_NAME=stub]') |
| 9 | + ' NUGET_PACKAGE_NAME [VERSION=latest] [WORK_DIR=tempDir]') |
8 | 10 | print(' The script uses the dotnet cli, codeql cli, and dotnet format global tool') |
9 | 11 |
|
10 | 12 | if len(sys.argv) < 2: |
|
15 | 17 | thisDir = os.path.abspath(os.path.dirname(thisScript)) |
16 | 18 | nuget = sys.argv[1] |
17 | 19 |
|
| 20 | +# /input contains a dotnet project that's being extracted |
18 | 21 | workDir = os.path.abspath(helpers.get_argv(3, "tempDir")) |
19 | 22 | projectNameIn = "input" |
20 | 23 | projectDirIn = os.path.join(workDir, projectNameIn) |
21 | 24 |
|
| 25 | +# /output contains the output of the stub generation |
| 26 | +outputDirName = "output" |
| 27 | +outputDir = os.path.join(workDir, outputDirName) |
| 28 | + |
| 29 | +# /output/raw contains the bqrs result from the query, the json equivalent |
| 30 | +rawOutputDirName = "raw" |
| 31 | +rawOutputDir = os.path.join(outputDir, rawOutputDirName) |
| 32 | +os.makedirs(rawOutputDir) |
| 33 | + |
| 34 | +# /output/output contains a dotnet project with the generated stubs |
22 | 35 | projectNameOut = "output" |
23 | | -projectDirOut = os.path.join(workDir, projectNameOut) |
| 36 | +projectDirOut = os.path.join(outputDir, projectNameOut) |
| 37 | + |
| 38 | +# /db contains the extracted QL DB |
24 | 39 | dbName = 'db' |
25 | 40 | dbDir = os.path.join(workDir, dbName) |
26 | | -outputName = helpers.get_argv(4, "stub") |
| 41 | +outputName = "stub" |
27 | 42 | outputFile = os.path.join(projectDirOut, outputName + '.cs') |
28 | | -bqrsFile = os.path.join(projectDirOut, outputName + '.bqrs') |
| 43 | +bqrsFile = os.path.join(rawOutputDir, outputName + '.bqrs') |
| 44 | +jsonFile = os.path.join(rawOutputDir, outputName + '.json') |
29 | 45 | version = helpers.get_argv(2, "latest") |
30 | 46 |
|
31 | 47 | print("\n* Creating new input project") |
32 | 48 | helpers.run_cmd(['dotnet', 'new', 'classlib', "--language", "C#", '--name', |
33 | 49 | projectNameIn, '--output', projectDirIn]) |
| 50 | +helpers.remove_files(projectDirIn, '.cs') |
34 | 51 |
|
35 | 52 | print("\n* Adding reference to package: " + nuget) |
36 | 53 | cmd = ['dotnet', 'add', projectDirIn, 'package', nuget] |
|
47 | 64 | print("Expected database directory " + dbDir + " not found.") |
48 | 65 | exit(1) |
49 | 66 |
|
50 | | -print("\n* Creating new output project") |
51 | | -helpers.run_cmd(['dotnet', 'new', 'classlib', "--language", "C#", '--name', |
52 | | - projectNameOut, '--output', projectDirOut]) |
53 | | - |
54 | 67 | print("\n* Running stubbing CodeQL query") |
55 | 68 | helpers.run_cmd(['codeql', 'query', 'run', os.path.join( |
56 | 69 | thisDir, 'AllStubsFromReference.ql'), '--database', dbDir, '--output', bqrsFile]) |
57 | 70 |
|
58 | 71 | helpers.run_cmd(['codeql', 'bqrs', 'decode', bqrsFile, '--output', |
59 | | - outputFile, '--format=text', '--no-titles']) |
60 | | - |
61 | | -helpers.trim_output_file(outputFile) |
62 | | - |
63 | | -print("\n --> Generated output file: " + outputFile) |
64 | | - |
65 | | -print("\n* Formatting file") |
66 | | -helpers.run_cmd(['dotnet', 'format', projectDirOut, |
67 | | - '--include', outputName + '.cs']) |
68 | | - |
69 | | -print("\n* Building output project") |
70 | | -helpers.run_cmd(['dotnet', 'build', '/t:rebuild', '/p:AllowUnsafeBlocks=true', projectDirOut], |
71 | | - 'ERR: Build failed. Script failed to generate a stub that builds') |
| 72 | + jsonFile, '--format=json']) |
| 73 | + |
| 74 | +print("\n* Creating new raw output project") |
| 75 | +rawSrcOutputDirName = 'src' |
| 76 | +rawSrcOutputDir = os.path.join(rawOutputDir, rawSrcOutputDirName) |
| 77 | +helpers.run_cmd(['dotnet', 'new', 'classlib', "--language", "C#", |
| 78 | + '--name', rawSrcOutputDirName, '--output', rawSrcOutputDir]) |
| 79 | +helpers.remove_files(rawSrcOutputDir, '.cs') |
| 80 | + |
| 81 | +# load json from file |
| 82 | +pathInfos = {} |
| 83 | +with open(jsonFile) as json_data: |
| 84 | + data = json.load(json_data) |
| 85 | + for row in data['#select']['tuples']: |
| 86 | + pathInfos[row[3]] = os.path.join(rawSrcOutputDir, row[1] + '.cs') |
| 87 | + with open(pathInfos[row[3]], 'a') as f: |
| 88 | + f.write(row[4]) |
| 89 | + |
| 90 | +print("\n --> Generated stub files: " + rawSrcOutputDir) |
| 91 | + |
| 92 | +print("\n* Formatting files") |
| 93 | +helpers.run_cmd(['dotnet', 'format', rawSrcOutputDir]) |
| 94 | + |
| 95 | +print("\n --> Generated (formatted) stub files: " + rawSrcOutputDir) |
| 96 | + |
| 97 | + |
| 98 | +print("\n* Processing project.assets.json to generate folder structure") |
| 99 | +stubsDirName = 'stubs' |
| 100 | +stubsDir = os.path.join(outputDir, stubsDirName) |
| 101 | +os.makedirs(stubsDir) |
| 102 | + |
| 103 | +stubFileName = '_stub.cs' |
| 104 | + |
| 105 | +frameworksDirName = 'frameworks' |
| 106 | +frameworksDir = os.path.join(stubsDir, frameworksDirName) |
| 107 | + |
| 108 | +frameworks = set() |
| 109 | +copiedFiles = set() |
| 110 | + |
| 111 | +assetsJsonFile = os.path.join(projectDirIn, 'obj', 'project.assets.json') |
| 112 | +with open(assetsJsonFile) as json_data: |
| 113 | + data = json.load(json_data) |
| 114 | + if len(data['targets']) > 1: |
| 115 | + print("ERROR: More than one target found in " + assetsJsonFile) |
| 116 | + exit(1) |
| 117 | + target = list(data['targets'].keys())[0] |
| 118 | + print("Found target: " + target) |
| 119 | + for package in data['targets'][target].keys(): |
| 120 | + parts = package.split('/') |
| 121 | + name = parts[0] |
| 122 | + version = parts[1] |
| 123 | + packageDir = os.path.join(stubsDir, name, version) |
| 124 | + if not os.path.exists(packageDir): |
| 125 | + os.makedirs(packageDir) |
| 126 | + print(' * Processing package: ' + name + '/' + version) |
| 127 | + with open(os.path.join(packageDir, stubFileName), 'a') as f: |
| 128 | + f.write('// Stub for ' + name + ' version ' + version + '\n\n') |
| 129 | + |
| 130 | + dlls = set() |
| 131 | + if 'compile' in data['targets'][target][package]: |
| 132 | + for dll in data['targets'][target][package]['compile']: |
| 133 | + dlls.add((name + '/' + version + '/' + dll).lower()) |
| 134 | + if 'runtime' in data['targets'][target][package]: |
| 135 | + for dll in data['targets'][target][package]['runtime']: |
| 136 | + dlls.add((name + '/' + version + '/' + dll).lower()) |
| 137 | + |
| 138 | + for pathInfo in pathInfos: |
| 139 | + for dll in dlls: |
| 140 | + if pathInfo.lower().endswith(dll): |
| 141 | + copiedFiles.add(pathInfo) |
| 142 | + shutil.copy2(pathInfos[pathInfo], packageDir) |
| 143 | + f.write('// semmle-extractor-options: ' + |
| 144 | + os.path.basename(pathInfos[pathInfo]) + '\n') |
| 145 | + |
| 146 | + if 'dependencies' in data['targets'][target][package]: |
| 147 | + for dependency in data['targets'][target][package]['dependencies'].keys(): |
| 148 | + depVersion = data['targets'][target][package]['dependencies'][dependency] |
| 149 | + f.write('// semmle-extractor-options: ../../' + |
| 150 | + dependency + '/' + depVersion + '/' + stubFileName + '\n') |
| 151 | + |
| 152 | + if 'frameworkReferences' in data['targets'][target][package]: |
| 153 | + if not os.path.exists(frameworksDir): |
| 154 | + os.makedirs(frameworksDir) |
| 155 | + for framework in data['targets'][target][package]['frameworkReferences']: |
| 156 | + frameworks.add(framework) |
| 157 | + frameworkDir = os.path.join(frameworksDir, framework) |
| 158 | + if not os.path.exists(frameworkDir): |
| 159 | + os.makedirs(frameworkDir) |
| 160 | + f.write('// semmle-extractor-options: ../../' + frameworksDirName + '/' + |
| 161 | + framework + '/' + stubFileName + '\n') |
| 162 | + |
| 163 | +for framework in frameworks: |
| 164 | + with open(os.path.join(frameworksDir, framework, stubFileName), 'a') as f: |
| 165 | + f.write('// Stub for ' + framework + '\n\n') |
| 166 | + for pathInfo in pathInfos: |
| 167 | + if 'packs/' + framework.lower() in pathInfo.lower(): |
| 168 | + copiedFiles.add(pathInfo) |
| 169 | + shutil.copy2(pathInfos[pathInfo], os.path.join( |
| 170 | + frameworksDir, framework)) |
| 171 | + f.write('// semmle-extractor-options: ' + |
| 172 | + os.path.basename(pathInfos[pathInfo]) + '\n') |
| 173 | + |
| 174 | +# todo: write not copied files to others folder |
| 175 | +for pathInfo in pathInfos: |
| 176 | + if pathInfo not in copiedFiles: |
| 177 | + print('Not copied to nuget or framework folder: ' + pathInfo) |
| 178 | + othersDir = os.path.join(stubsDir, 'others') |
| 179 | + if not os.path.exists(othersDir): |
| 180 | + os.makedirs(othersDir) |
| 181 | + shutil.copy2(pathInfos[pathInfo], othersDir) |
| 182 | + |
| 183 | +print("\n --> Generated structured stub files: " + stubsDir) |
| 184 | + |
| 185 | +print("\n* Building raw output project") |
| 186 | +helpers.run_cmd(['dotnet', 'build', '/t:rebuild', '/p:AllowUnsafeBlocks=true', '/p:WarningLevel=0', |
| 187 | + rawSrcOutputDir], 'ERR: Build failed. Script failed to generate a stub that builds') |
| 188 | + |
| 189 | +print("\n --> Generated structured stub files: " + stubsDir) |
72 | 190 |
|
73 | | -print("\n --> Generated output file: " + outputFile) |
74 | 191 | exit(0) |
0 commit comments