/// <summary> /// Resolves metadata references stored in command line arguments and reports errors for those that can't be resolved. /// </summary> internal List <MetadataReference> ResolveMetadataReferences( MetadataFileReferenceResolver externalReferenceResolver, MetadataFileReferenceProvider metadataProvider, List <DiagnosticInfo> diagnostics, AssemblyIdentityComparer assemblyIdentityComparer, TouchedFileLogger touchedFiles, out MetadataFileReferenceResolver referenceDirectiveResolver) { List <MetadataReference> resolved = new List <MetadataReference>(); Arguments.ResolveMetadataReferences(new AssemblyReferenceResolver(externalReferenceResolver, metadataProvider), diagnostics, this.MessageProvider, resolved); if (Arguments.IsInteractive) { referenceDirectiveResolver = externalReferenceResolver; } else { // when compiling into an assembly (csc/vbc) we only allow #r that match references given on command line: referenceDirectiveResolver = new ExistingReferencesResolver( resolved.Where(r => r.Properties.Kind == MetadataImageKind.Assembly).OfType <PortableExecutableReference>().AsImmutable(), Arguments.ReferencePaths, Arguments.BaseDirectory, assemblyIdentityComparer, touchedFiles); } return(resolved); }
public ExistingReferencesResolver( MetadataFileReferenceResolver resolver, ImmutableArray <PortableExecutableReference> availableReferences, AssemblyIdentityComparer assemblyIdentityComparer) { Debug.Assert(!availableReferences.Any(r => r.Properties.Kind != MetadataImageKind.Assembly)); _resolver = resolver; _availableReferences = availableReferences; _assemblyIdentityComparer = assemblyIdentityComparer; }
public ExistingReferencesResolver( MetadataFileReferenceResolver resolver, ImmutableArray<PortableExecutableReference> availableReferences, AssemblyIdentityComparer assemblyIdentityComparer) { Debug.Assert(!availableReferences.Any(r => r.Properties.Kind != MetadataImageKind.Assembly)); _resolver = resolver; _availableReferences = availableReferences; _assemblyIdentityComparer = assemblyIdentityComparer; }
/// <summary> /// Create a ProjectInfo structure initialized from a compilers command line arguments. /// </summary> public static ProjectInfo CreateProjectInfo(Workspace workspace, string projectName, string language, IEnumerable <string> commandLineArgs, string projectDirectory) { // TODO (tomat): the method may throw all sorts of exceptions. var languageServices = workspace.Services.GetLanguageServices(language); if (languageServices == null) { throw new ArgumentException(WorkspacesResources.UnrecognizedLanguageName); } var commandLineArgumentsFactory = languageServices.GetService <ICommandLineArgumentsFactoryService>(); var commandLineArguments = commandLineArgumentsFactory.CreateCommandLineArguments(commandLineArgs, projectDirectory, isInteractive: false); // TODO (tomat): to match csc.exe/vbc.exe we should use CommonCommandLineCompiler.ExistingReferencesResolver to deal with #r's var referenceResolver = new MetadataFileReferenceResolver(commandLineArguments.ReferencePaths, commandLineArguments.BaseDirectory); var referenceProvider = MetadataFileReferenceProvider.Default; var xmlFileResolver = new XmlFileResolver(commandLineArguments.BaseDirectory); var strongNameProvider = new DesktopStrongNameProvider(commandLineArguments.KeyFileSearchPaths); // resolve all metadata references. var boundMetadataReferences = commandLineArguments.ResolveMetadataReferences(referenceResolver, referenceProvider); var unresolvedMetadataReferences = boundMetadataReferences.FirstOrDefault(r => r is UnresolvedMetadataReference); if (unresolvedMetadataReferences != null) { throw new ArgumentException(string.Format(WorkspacesResources.CantResolveMetadataReference, ((UnresolvedMetadataReference)unresolvedMetadataReferences).Reference)); } // resolve all analyzer references. var boundAnalyzerReferences = commandLineArguments.ResolveAnalyzerReferences(); var unresolvedAnalyzerReferences = boundAnalyzerReferences.FirstOrDefault(r => r is UnresolvedAnalyzerReference); if (unresolvedAnalyzerReferences != null) { throw new ArgumentException(string.Format(WorkspacesResources.CantResolveAnalyzerReference, ((UnresolvedAnalyzerReference)unresolvedAnalyzerReferences).Display)); } AssemblyIdentityComparer assemblyIdentityComparer; if (commandLineArguments.AppConfigPath != null) { try { using (var appConfigStream = new FileStream(commandLineArguments.AppConfigPath, FileMode.Open, FileAccess.Read)) { assemblyIdentityComparer = DesktopAssemblyIdentityComparer.LoadFromXml(appConfigStream); } } catch (Exception e) { throw new ArgumentException(string.Format(WorkspacesResources.ErrorWhileReadingSpecifiedConfigFile, e.Message)); } } else { assemblyIdentityComparer = DesktopAssemblyIdentityComparer.Default; } var projectId = ProjectId.CreateNewId(debugName: projectName); // construct file infos var docs = new List <DocumentInfo>(); var ids = new HashSet <DocumentId>(); foreach (var fileArg in commandLineArguments.SourceFiles) { var absolutePath = Path.IsPathRooted(fileArg.Path) || string.IsNullOrEmpty(projectDirectory) ? Path.GetFullPath(fileArg.Path) : Path.GetFullPath(Path.Combine(projectDirectory, fileArg.Path)); var relativePath = FilePathUtilities.GetRelativePath(projectDirectory, absolutePath); var isWithinProject = !Path.IsPathRooted(relativePath); var folderRoot = isWithinProject ? Path.GetDirectoryName(relativePath) : ""; var folders = isWithinProject ? GetFolders(relativePath) : null; var name = Path.GetFileName(relativePath); var id = GetUniqueDocumentId(projectId, name, folderRoot, ids); var doc = DocumentInfo.Create( id: id, name: name, folders: folders, sourceCodeKind: fileArg.IsScript ? SourceCodeKind.Script : SourceCodeKind.Regular, loader: new FileTextLoader(absolutePath), filePath: absolutePath); docs.Add(doc); } // If /out is not specified and the project is a console app the csc.exe finds out the Main method // and names the compilation after the file that contains it. We don't want to create a compilation, // bind Mains etc. here. Besides the msbuild always includes /out in the command line it produces. // So if we don't have the /out argument we name the compilation "<anonymous>". string assemblyName = (commandLineArguments.OutputFileName != null) ? Path.GetFileNameWithoutExtension(commandLineArguments.OutputFileName) : "<anonymous>"; // TODO (tomat): what should be the assemblyName when compiling a netmodule? Should it be /moduleassemblyname var projectInfo = ProjectInfo.Create( projectId, VersionStamp.Create(), projectName, assemblyName, language: language, compilationOptions: commandLineArguments.CompilationOptions .WithXmlReferenceResolver(xmlFileResolver) .WithAssemblyIdentityComparer(assemblyIdentityComparer) .WithStrongNameProvider(strongNameProvider) .WithMetadataReferenceResolver(referenceResolver) .WithMetadataReferenceProvider(referenceProvider), parseOptions: commandLineArguments.ParseOptions, documents: docs, metadataReferences: boundMetadataReferences, analyzerReferences: boundAnalyzerReferences); return(projectInfo); }
/// <summary> /// Create a <see cref="ProjectInfo"/> structure initialized from a compilers command line arguments. /// </summary> public static ProjectInfo CreateProjectInfo(string projectName, string language, IEnumerable<string> commandLineArgs, string projectDirectory, Workspace workspace = null) { // TODO (tomat): the method may throw all sorts of exceptions. var tmpWorkspace = workspace ?? new AdhocWorkspace(DesktopMefHostServices.DefaultServices); var languageServices = tmpWorkspace.Services.GetLanguageServices(language); if (languageServices == null) { throw new ArgumentException(WorkspacesResources.UnrecognizedLanguageName); } var commandLineArgumentsFactory = languageServices.GetRequiredService<ICommandLineArgumentsFactoryService>(); var commandLineArguments = commandLineArgumentsFactory.CreateCommandLineArguments(commandLineArgs, projectDirectory, isInteractive: false); // TODO (tomat): to match csc.exe/vbc.exe we should use CommonCommandLineCompiler.ExistingReferencesResolver to deal with #r's var referenceResolver = new MetadataFileReferenceResolver(commandLineArguments.ReferencePaths, commandLineArguments.BaseDirectory); var referenceProvider = tmpWorkspace.Services.GetRequiredService<IMetadataService>().GetProvider(); var analyzerService = tmpWorkspace.Services.GetRequiredService<IAnalyzerService>(); var xmlFileResolver = new XmlFileResolver(commandLineArguments.BaseDirectory); var strongNameProvider = new DesktopStrongNameProvider(commandLineArguments.KeyFileSearchPaths); // resolve all metadata references. var boundMetadataReferences = commandLineArguments.ResolveMetadataReferences(new AssemblyReferenceResolver(referenceResolver, referenceProvider)); var unresolvedMetadataReferences = boundMetadataReferences.FirstOrDefault(r => r is UnresolvedMetadataReference); if (unresolvedMetadataReferences != null) { throw new ArgumentException(string.Format(WorkspacesResources.CantResolveMetadataReference, ((UnresolvedMetadataReference)unresolvedMetadataReferences).Reference)); } // resolve all analyzer references. var boundAnalyzerReferences = commandLineArguments.ResolveAnalyzerReferences(analyzerService.GetAnalyzer); var unresolvedAnalyzerReferences = boundAnalyzerReferences.FirstOrDefault(r => r is UnresolvedAnalyzerReference); if (unresolvedAnalyzerReferences != null) { throw new ArgumentException(string.Format(WorkspacesResources.CantResolveAnalyzerReference, ((UnresolvedAnalyzerReference)unresolvedAnalyzerReferences).Display)); } AssemblyIdentityComparer assemblyIdentityComparer; if (commandLineArguments.AppConfigPath != null) { try { using (var appConfigStream = new FileStream(commandLineArguments.AppConfigPath, FileMode.Open, FileAccess.Read)) { assemblyIdentityComparer = DesktopAssemblyIdentityComparer.LoadFromXml(appConfigStream); } } catch (Exception e) { throw new ArgumentException(string.Format(WorkspacesResources.ErrorWhileReadingSpecifiedConfigFile, e.Message)); } } else { assemblyIdentityComparer = DesktopAssemblyIdentityComparer.Default; } var projectId = ProjectId.CreateNewId(debugName: projectName); // construct file infos var docs = new List<DocumentInfo>(); foreach (var fileArg in commandLineArguments.SourceFiles) { var absolutePath = Path.IsPathRooted(fileArg.Path) || string.IsNullOrEmpty(projectDirectory) ? Path.GetFullPath(fileArg.Path) : Path.GetFullPath(Path.Combine(projectDirectory, fileArg.Path)); var relativePath = FilePathUtilities.GetRelativePath(projectDirectory, absolutePath); var isWithinProject = FilePathUtilities.IsNestedPath(projectDirectory, absolutePath); var folderRoot = isWithinProject ? Path.GetDirectoryName(relativePath) : ""; var folders = isWithinProject ? GetFolders(relativePath) : null; var name = Path.GetFileName(relativePath); var id = DocumentId.CreateNewId(projectId, absolutePath); var doc = DocumentInfo.Create( id: id, name: name, folders: folders, sourceCodeKind: fileArg.IsScript ? SourceCodeKind.Script : SourceCodeKind.Regular, loader: new FileTextLoader(absolutePath, commandLineArguments.Encoding), filePath: absolutePath); docs.Add(doc); } // construct file infos for additional files. var additionalDocs = new List<DocumentInfo>(); foreach (var fileArg in commandLineArguments.AdditionalFiles) { var absolutePath = Path.IsPathRooted(fileArg.Path) || string.IsNullOrEmpty(projectDirectory) ? Path.GetFullPath(fileArg.Path) : Path.GetFullPath(Path.Combine(projectDirectory, fileArg.Path)); var relativePath = FilePathUtilities.GetRelativePath(projectDirectory, absolutePath); var isWithinProject = FilePathUtilities.IsNestedPath(projectDirectory, absolutePath); var folderRoot = isWithinProject ? Path.GetDirectoryName(relativePath) : ""; var folders = isWithinProject ? GetFolders(relativePath) : null; var name = Path.GetFileName(relativePath); var id = DocumentId.CreateNewId(projectId, absolutePath); var doc = DocumentInfo.Create( id: id, name: name, folders: folders, sourceCodeKind: SourceCodeKind.Regular, loader: new FileTextLoader(absolutePath, commandLineArguments.Encoding), filePath: absolutePath); additionalDocs.Add(doc); } // If /out is not specified and the project is a console app the csc.exe finds out the Main method // and names the compilation after the file that contains it. We don't want to create a compilation, // bind Mains etc. here. Besides the msbuild always includes /out in the command line it produces. // So if we don't have the /out argument we name the compilation "<anonymous>". string assemblyName = (commandLineArguments.OutputFileName != null) ? Path.GetFileNameWithoutExtension(commandLineArguments.OutputFileName) : "<anonymous>"; // TODO (tomat): what should be the assemblyName when compiling a netmodule? Should it be /moduleassemblyname var projectInfo = ProjectInfo.Create( projectId, VersionStamp.Create(), projectName, assemblyName, language: language, compilationOptions: commandLineArguments.CompilationOptions .WithXmlReferenceResolver(xmlFileResolver) .WithAssemblyIdentityComparer(assemblyIdentityComparer) .WithStrongNameProvider(strongNameProvider) .WithMetadataReferenceResolver(new AssemblyReferenceResolver(referenceResolver, referenceProvider)), parseOptions: commandLineArguments.ParseOptions, documents: docs, additionalDocuments: additionalDocs, metadataReferences: boundMetadataReferences, analyzerReferences: boundAnalyzerReferences); return projectInfo; }
private void GetReferences( CSharpCompilerInputs compilerInputs, MSB.Execution.ProjectInstance executedProject, out IEnumerable<MetadataReference> metadataReferences, out IEnumerable<AnalyzerReference> analyzerReferences) { // use command line parser to do reference translation same as command line compiler var args = new List<string>(); if (compilerInputs.LibPaths != null && compilerInputs.LibPaths.Count > 0) { args.Add("/lib:\"" + string.Join(";", compilerInputs.LibPaths) + "\""); } foreach (var mr in compilerInputs.References) { if (!IsProjectReferenceOutputAssembly(mr)) { var filePath = GetDocumentFilePath(mr); var aliases = GetAliases(mr); if (aliases.IsDefaultOrEmpty) { args.Add("/r:\"" + filePath + "\""); } else { foreach (var alias in aliases) { args.Add("/r:" + alias + "=\"" + filePath + "\""); } } } } foreach (var ar in compilerInputs.AnalyzerReferences) { var filePath = GetDocumentFilePath(ar); args.Add("/a:\"" + filePath + "\""); } if (compilerInputs.NoStandardLib) { args.Add("/nostdlib"); } var commandLineArgs = _commandLineArgumentsFactoryService.CreateCommandLineArguments(args, executedProject.Directory, isInteractive: false, sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory()); var resolver = new MetadataFileReferenceResolver(commandLineArgs.ReferencePaths, commandLineArgs.BaseDirectory); metadataReferences = commandLineArgs.ResolveMetadataReferences(new AssemblyReferenceResolver(resolver, _metadataService.GetProvider())); var analyzerLoader = _analyzerService.GetLoader(); foreach (var path in commandLineArgs.AnalyzerReferences.Select(r => r.FilePath)) { analyzerLoader.AddDependencyLocation(path); } analyzerReferences = commandLineArgs.ResolveAnalyzerReferences(analyzerLoader); }
/// <summary> /// Resolves metadata references stored in command line arguments and reports errors for those that can't be resolved. /// </summary> internal List<MetadataReference> ResolveMetadataReferences( MetadataFileReferenceResolver externalReferenceResolver, MetadataFileReferenceProvider metadataProvider, List<DiagnosticInfo> diagnostics, AssemblyIdentityComparer assemblyIdentityComparer, TouchedFileLogger touchedFiles, out MetadataFileReferenceResolver referenceDirectiveResolver) { List<MetadataReference> resolved = new List<MetadataReference>(); Arguments.ResolveMetadataReferences(new AssemblyReferenceResolver(externalReferenceResolver, metadataProvider), diagnostics, this.MessageProvider, resolved); if (Arguments.IsInteractive) { referenceDirectiveResolver = externalReferenceResolver; } else { // when compiling into an assembly (csc/vbc) we only allow #r that match references given on command line: referenceDirectiveResolver = new ExistingReferencesResolver( CreateLoggingMetadataResolver(touchedFiles), resolved.Where(r => r.Properties.Kind == MetadataImageKind.Assembly).OfType<PortableExecutableReference>().AsImmutable(), assemblyIdentityComparer); } return resolved; }
public LoggingMetadataReferencesResolver(MetadataFileReferenceResolver resolver, TouchedFileLogger logger) { Debug.Assert(logger != null); _resolver = resolver; _logger = logger; }
public AssemblyReferenceResolver(MetadataFileReferenceResolver pathResolver, MetadataFileReferenceProvider provider) { Debug.Assert(pathResolver != null && provider != null); this.PathResolver = pathResolver; this.Provider = provider; }
private void GetReferences( CSharpCompilerInputs compilerInputs, MSB.Execution.ProjectInstance executedProject, out IEnumerable<MetadataReference> metadataReferences, out IEnumerable<AnalyzerReference> analyzerReferences) { // use command line parser to do reference translation same as command line compiler var args = new List<string>(); if (compilerInputs.LibPaths != null && compilerInputs.LibPaths.Count > 0) { args.Add("/lib:\"" + string.Join(";", compilerInputs.LibPaths) + "\""); } foreach (var mr in compilerInputs.References) { var filePath = GetDocumentFilePath(mr); var aliases = GetAliases(mr); if (aliases.IsDefaultOrEmpty) { args.Add("/r:\"" + filePath + "\""); } else { foreach (var alias in aliases) { args.Add("/r:" + alias + "=\"" + filePath + "\""); } } } foreach (var ar in compilerInputs.AnalyzerReferences) { var filePath = GetDocumentFilePath(ar); args.Add("/a:\"" + filePath + "\""); } if (compilerInputs.NoStandardLib) { args.Add("/nostdlib"); } var commandLineParser = CSharpCommandLineParser.Default; var commandLineArgs = commandLineParser.Parse(args, executedProject.Directory); var resolver = new MetadataFileReferenceResolver(commandLineArgs.ReferencePaths, commandLineArgs.BaseDirectory); metadataReferences = commandLineArgs.ResolveMetadataReferences(new AssemblyReferenceResolver(resolver, _metadataService.GetProvider())); analyzerReferences = commandLineArgs.ResolveAnalyzerReferences(); }
/// <summary> /// Creates a new <see cref="ScriptOptions"/> with the reference resolver specified. /// </summary> internal ScriptOptions WithReferenceResolver(MetadataFileReferenceResolver resolver) { if (resolver == _referenceResolver.PathResolver) { return this; } return With(resolver: new AssemblyReferenceResolver(resolver, _referenceResolver.Provider)); }
private async Task<ProjectId> LoadProjectAsync(string projectFilePath, IProjectFileLoader loader, bool preferMetadata, Dictionary<ProjectId, ProjectInfo> loadedProjects, CancellationToken cancellationToken) { Debug.Assert(projectFilePath != null); Debug.Assert(loader != null); var projectId = this.GetOrCreateProjectId(projectFilePath); var projectName = Path.GetFileNameWithoutExtension(projectFilePath); var projectFile = await loader.LoadProjectFileAsync(projectFilePath, _properties, cancellationToken).ConfigureAwait(false); var projectFileInfo = await projectFile.GetProjectFileInfoAsync(cancellationToken).ConfigureAwait(false); var projectDirectory = Path.GetDirectoryName(projectFilePath); var outputFilePath = projectFileInfo.OutputFilePath; var outputDirectory = Path.GetDirectoryName(outputFilePath); VersionStamp version; if (!string.IsNullOrEmpty(projectFilePath) && File.Exists(projectFilePath)) { version = VersionStamp.Create(File.GetLastWriteTimeUtc(projectFilePath)); } else { version = VersionStamp.Create(); } // translate information from command line args var commandLineParser = this.Services.GetLanguageServices(loader.Language).GetService<ICommandLineParserService>(); var metadataService = this.Services.GetService<IMetadataService>(); var analyzerService = this.Services.GetService<IAnalyzerService>(); var commandLineArgs = commandLineParser.Parse( arguments: projectFileInfo.CommandLineArgs, baseDirectory: projectDirectory, isInteractive: false, sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory()); var resolver = new MetadataFileReferenceResolver(commandLineArgs.ReferencePaths, commandLineArgs.BaseDirectory); var metadataReferences = commandLineArgs.ResolveMetadataReferences(new AssemblyReferenceResolver(resolver, metadataService.GetProvider())); var analyzerLoader = analyzerService.GetLoader(); foreach (var path in commandLineArgs.AnalyzerReferences.Select(r => r.FilePath)) { analyzerLoader.AddDependencyLocation(path); } var analyzerReferences = commandLineArgs.ResolveAnalyzerReferences(analyzerLoader); var defaultEncoding = commandLineArgs.Encoding; // docs & additional docs var docFileInfos = projectFileInfo.Documents.ToImmutableArrayOrEmpty(); var additionalDocFileInfos = projectFileInfo.AdditionalDocuments.ToImmutableArrayOrEmpty(); // check for duplicate documents var allDocFileInfos = docFileInfos.AddRange(additionalDocFileInfos); CheckDocuments(allDocFileInfos, projectFilePath, projectId); var docs = new List<DocumentInfo>(); foreach (var docFileInfo in docFileInfos) { string name; ImmutableArray<string> folders; GetDocumentNameAndFolders(docFileInfo.LogicalPath, out name, out folders); docs.Add(DocumentInfo.Create( DocumentId.CreateNewId(projectId, debugName: docFileInfo.FilePath), name, folders, projectFile.GetSourceCodeKind(docFileInfo.FilePath), new FileTextLoader(docFileInfo.FilePath, defaultEncoding), docFileInfo.FilePath, docFileInfo.IsGenerated)); } var additionalDocs = new List<DocumentInfo>(); foreach (var docFileInfo in additionalDocFileInfos) { string name; ImmutableArray<string> folders; GetDocumentNameAndFolders(docFileInfo.LogicalPath, out name, out folders); additionalDocs.Add(DocumentInfo.Create( DocumentId.CreateNewId(projectId, debugName: docFileInfo.FilePath), name, folders, SourceCodeKind.Regular, new FileTextLoader(docFileInfo.FilePath, defaultEncoding), docFileInfo.FilePath, docFileInfo.IsGenerated)); } // project references var resolvedReferences = await this.ResolveProjectReferencesAsync( projectId, projectFilePath, projectFileInfo.ProjectReferences, preferMetadata, loadedProjects, cancellationToken).ConfigureAwait(false); // add metadata references for project refs converted to metadata refs metadataReferences = metadataReferences.Concat(resolvedReferences.MetadataReferences); // if the project file loader couldn't figure out an assembly name, make one using the project's file path. var assemblyName = commandLineArgs.CompilationName; if (string.IsNullOrWhiteSpace(assemblyName)) { assemblyName = Path.GetFileNameWithoutExtension(projectFilePath); // if this is still unreasonable, use a fixed name. if (string.IsNullOrWhiteSpace(assemblyName)) { assemblyName = "assembly"; } } // make sure that doc-comments at least get parsed. var parseOptions = commandLineArgs.ParseOptions; if (parseOptions.DocumentationMode == DocumentationMode.None) { parseOptions = parseOptions.WithDocumentationMode(DocumentationMode.Parse); } // add all the extra options that are really behavior overrides var compOptions = commandLineArgs.CompilationOptions .WithXmlReferenceResolver(new XmlFileResolver(projectDirectory)) .WithSourceReferenceResolver(new SourceFileResolver(ImmutableArray<string>.Empty, projectDirectory)) .WithMetadataReferenceResolver( new AssemblyReferenceResolver( new MetadataFileReferenceResolver(ImmutableArray<string>.Empty, projectDirectory), MetadataFileReferenceProvider.Default)) .WithStrongNameProvider(new DesktopStrongNameProvider(ImmutableArray.Create(projectDirectory, outputFilePath))) .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default); loadedProjects.Add( projectId, ProjectInfo.Create( projectId, version, projectName, assemblyName, loader.Language, projectFilePath, outputFilePath, compilationOptions: compOptions, parseOptions: parseOptions, documents: docs, projectReferences: resolvedReferences.ProjectReferences, metadataReferences: metadataReferences, analyzerReferences: analyzerReferences, additionalDocuments: additionalDocs, isSubmission: false, hostObjectType: null)); return projectId; }