@@ -110,6 +110,19 @@ def main():
110
110
help = "Exclude files matching this pattern from the code base. "
111
111
+ "May be specified multiple times." ,
112
112
)
113
+ parser .add_argument (
114
+ "-p" ,
115
+ "--platform" ,
116
+ dest = "platforms" ,
117
+ metavar = "<platform>" ,
118
+ action = "append" ,
119
+ default = [],
120
+ help = "Add the specified platform to the analysis. "
121
+ + "May be a name or a path to a compilation database. "
122
+ + "May be specified multiple times. "
123
+ + "If not specified, all known platforms will be included." ,
124
+ )
125
+
113
126
args = parser .parse_args ()
114
127
115
128
stdout_log = logging .StreamHandler (sys .stdout )
@@ -120,25 +133,76 @@ def main():
120
133
)
121
134
rootdir = os .path .realpath (args .rootdir )
122
135
123
- if args .config_file is None :
136
+ # Process the -p flag first to infer wider context.
137
+ filtered_platforms = []
138
+ additional_platforms = []
139
+ for p in args .platforms :
140
+ # If it's a path, it has to be a compilation database.
141
+ if os .path .exists (p ):
142
+ if not os .path .splitext (p )[1 ] == ".json" :
143
+ raise RuntimeError (f"Platform file { p } must end in .json." )
144
+ additional_platforms .append (p )
145
+ continue
146
+
147
+ # Otherwise, treat it as a name in the configuration file.
148
+ if not isinstance (p , str ):
149
+ raise RuntimeError (f"Platform name { p } must be a string." )
150
+
151
+ # Explain the logic above in cases that look suspiciously like paths.
152
+ if "/" in p or os .path .splitext (p )[1 ] == ".json" :
153
+ logging .getLogger ("codebasin" ).warning (
154
+ f"{ p } doesn't exist, so will be treated as a name." ,
155
+ )
156
+ filtered_platforms .append (p )
157
+
158
+ # If no additional platforms are specified, a config file is required.
159
+ config_file = args .config_file
160
+ if len (additional_platforms ) == 0 and config_file is None :
124
161
config_file = os .path .join (rootdir , "config.yaml" )
125
- else :
126
- config_file = args .config_file
127
- # Load the configuration file into a dict
128
- if not util .ensure_yaml (config_file ):
129
- logging .getLogger ("codebasin" ).error (
130
- "Configuration file does not have YAML file extension." ,
162
+ if not os .path .exists (config_file ):
163
+ raise RuntimeError (f"Could not find config file { config_file } " )
164
+
165
+ # Set up a default codebase and configuration object.
166
+ codebase = {
167
+ "files" : [],
168
+ "platforms" : [],
169
+ "exclude_files" : set (),
170
+ "exclude_patterns" : args .excludes ,
171
+ "rootdir" : rootdir ,
172
+ }
173
+ configuration = {}
174
+
175
+ # Load the configuration file if it exists, obeying any platform filter.
176
+ if config_file is not None :
177
+ if not util .ensure_yaml (config_file ):
178
+ logging .getLogger ("codebasin" ).error (
179
+ "Configuration file does not have YAML file extension." ,
180
+ )
181
+ sys .exit (1 )
182
+ codebase , configuration = config .load (
183
+ config_file ,
184
+ rootdir ,
185
+ exclude_patterns = args .excludes ,
186
+ filtered_platforms = filtered_platforms ,
131
187
)
132
- sys .exit (1 )
133
- codebase , configuration = config .load (
134
- config_file ,
135
- rootdir ,
136
- exclude_patterns = args .excludes ,
137
- )
188
+
189
+ # Extend configuration with any additional platforms.
190
+ for p in additional_platforms :
191
+ name = os .path .splitext (os .path .basename (p ))[0 ]
192
+ if name in codebase ["platforms" ]:
193
+ raise RuntimeError (f"Platform name { p } conflicts with { name } ." )
194
+ db = config .load_database (p , rootdir )
195
+ configuration .update ({name : db })
138
196
139
197
# Parse the source tree, and determine source line associations.
140
198
# The trees and associations are housed in state.
141
- state = finder .find (rootdir , codebase , configuration )
199
+ legacy_warnings = True if config_file else False
200
+ state = finder .find (
201
+ rootdir ,
202
+ codebase ,
203
+ configuration ,
204
+ legacy_warnings = legacy_warnings ,
205
+ )
142
206
143
207
# Count lines for platforms
144
208
platform_mapper = PlatformMapper (codebase )
@@ -172,7 +236,11 @@ def report_enabled(name):
172
236
173
237
# Print clustering report
174
238
if report_enabled ("clustering" ):
175
- output_prefix = os .path .realpath (guess_project_name (config_file ))
239
+ if config_file is None :
240
+ platform_names = [p [0 ] for p in args .platforms ]
241
+ output_prefix = "-" .join (platform_names )
242
+ else :
243
+ output_prefix = os .path .realpath (guess_project_name (config_file ))
176
244
clustering_output_name = output_prefix + "-dendrogram.png"
177
245
clustering = report .clustering (clustering_output_name , setmap )
178
246
if clustering is not None :
0 commit comments