/// <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 = LanguageService.GetProvider(workspace, 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  = workspace.CurrentSolution.MetadataReferenceProvider;
            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);
        }
Exemple #2
0
 /// <summary>
 /// Creates a new solution instance that includes a project with the specified language and names.
 /// </summary>
 public Solution AddProject(ProjectId projectId, string name, string assemblyName, string language)
 {
     return(this.AddProject(ProjectInfo.Create(projectId, VersionStamp.Create(), name, assemblyName, language)));
 }
Exemple #3
0
 /// <summary>
 /// Create a new empty solution instance associated with this workspace.
 /// </summary>
 protected internal Solution CreateSolution(SolutionId id)
 {
     return(CreateSolution(SolutionInfo.Create(id, VersionStamp.Create())));
 }
Exemple #4
0
        /// <summary>
        /// Load a text and a version of the document in the workspace.
        /// </summary>
        /// <exception cref="IOException"></exception>
        /// <exception cref="InvalidDataException"></exception>
        public override async Task <TextAndVersion> LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken)
        {
            ValidateFileLength(Path);

            var prevLastWriteTime = FileUtilities.GetFileTimeStamp(Path);

            TextAndVersion textAndVersion;

            // In many .NET Framework versions (specifically the 4.5.* series, but probably much earlier
            // and also later) there is this particularly interesting bit in FileStream.BeginReadAsync:
            //
            //     // [ed: full comment clipped for brevity]
            //     //
            //     // If we did a sync read to fill the buffer, we could avoid the
            //     // problem, and any async read less than 64K gets turned into a
            //     // synchronous read by NT anyways...
            //     if (numBytes < _bufferSize)
            //     {
            //         if (_buffer == null) _buffer = new byte[_bufferSize];
            //         IAsyncResult bufferRead = BeginReadCore(_buffer, 0, _bufferSize, null, null, 0);
            //         _readLen = EndRead(bufferRead);
            //
            // In English, this means that if you do a asynchronous read for smaller than _bufferSize,
            // this is implemented by the framework by starting an asynchronous read, and then
            // blocking your thread until that read is completed. The comment implies this is "fine"
            // because the asynchronous read will actually be synchronous and thus EndRead won't do
            // any blocking -- it'll be an effective no-op. In theory, everything is fine here.
            //
            // In reality, this can end very poorly. That read in fact can be asynchronous, which means the
            // EndRead will enter a wait and block the thread. If we are running that call to ReadAsync on a
            // thread pool thread that completed a previous piece of IO, it means there has to be another
            // thread available to service the completion of that request in order for our thread to make
            // progress. Why is this worse than the claim about the operating system turning an
            // asynchronous read into a synchronous one? If the underlying native ReadFile completes
            // synchronously, that would mean just our thread is being blocked, and will be unblocked once
            // the kernel gets done with our work. In this case, if the OS does do the read asynchronously
            // we are now dependent on another thread being available to unblock us.
            //
            // So how does ths manifest itself? We have seen dumps from customers reporting hangs where
            // we have over a hundred thread pool threads all blocked on EndRead() calls as we read this stream.
            // In these cases, the user had just completed a build that had a bunch of XAML files, and
            // this resulted in many .g.i.cs files being written and updated. As a result, Roslyn is trying to
            // re-read them to provide a new compilation to the XAML language service that is asking for it.
            // Inspecting these dumps and sampling some of the threads made some notable discoveries:
            //
            // 1. When there was a read blocked, it was the _last_ chunk that we were reading in the file in
            //    the file that we were reading. This leads me to believe that it isn't simply very slow IO
            //    (like a network drive), because in that case I'd expect to see some threads in different
            //    places than others.
            // 2. Some stacks were starting by the continuation of a ReadAsync, and some were the first read
            //    of a file from the background parser. In the first case, all of those threads were if the
            //    files were over 4K in size. The ones with the BackgroundParser still on the stack were files
            //    less than 4K in size.
            // 3. The "time unresponsive" in seconds correlated with roughly the number of threads we had
            //    blocked, which makes me think we were impacted by the once-per-second hill climbing algorithm
            //    used by the thread pool.
            //
            // So what's my analysis? When the XAML language service updated all the files, we kicked off
            // background parses for all of them. If the file was over 4K the asynchronous read actually did
            // happen (see point #2), but we'd eventually block the thread pool reading the last chunk.
            // Point #1 confirms that it was always the last chunk. And in small file cases, we'd block on
            // the first chunk. But in either case, we'd be blocking off a thread pool thread until another
            // thread pool thread was available. Since we had enough requests going (over a hundred),
            // sometimes the user got unlucky and all the threads got blocked. At this point, the CLR
            // started slowly kicking off more threads, but each time it'd start a new thread rather than
            // starting work that would be needed to unblock a thread, it just handled an IO that resulted
            // in another file read hitting the end of the file and another thread would get blocked. The
            // CLR then must kick off another thread, rinse, repeat. Eventually it'll make progress once
            // there's no more pending IO requests, everything will complete, and life then continues.
            //
            // To work around this issue, we set bufferSize to 1, which means that all reads should bypass
            // this logic. This is tracked by https://github.com/dotnet/corefx/issues/6007, at least in
            // corefx. We also open the file for reading with FileShare mode read/write/delete so that
            // we do not lock this file.
            using (var stream = FileUtilities.RethrowExceptionsAsIOException(() => new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete, bufferSize: 1, useAsync: true)))
            {
                var version = VersionStamp.Create(prevLastWriteTime);

                // we do this so that we asynchronously read from file. and this should allocate less for IDE case.
                // but probably not for command line case where it doesn't use more sophisticated services.
                using var readStream = await SerializableBytes.CreateReadableStreamAsync(stream, cancellationToken : cancellationToken).ConfigureAwait(false);

                var text = CreateText(readStream, workspace);
                textAndVersion = TextAndVersion.Create(text, version, Path);
            }

            // Check if the file was definitely modified and closed while we were reading. In this case, we know the read we got was
            // probably invalid, so throw an IOException which indicates to our caller that we should automatically attempt a re-read.
            // If the file hasn't been closed yet and there's another writer, we will rely on file change notifications to notify us
            // and reload the file.
            var newLastWriteTime = FileUtilities.GetFileTimeStamp(Path);

            if (!newLastWriteTime.Equals(prevLastWriteTime))
            {
                var message = string.Format(WorkspacesResources.File_was_externally_modified_colon_0, Path);
                throw new IOException(message);
            }

            return(textAndVersion);
        }
Exemple #5
0
        /// <summary>
        /// Adds a project to the workspace. All previous projects remain intact.
        /// </summary>
        public Project AddProject(string name, string language)
        {
            var info = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), name, name, language);

            return(this.AddProject(info));
        }
Exemple #6
0
        /// <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.Unrecognized_language_name);
            }

            var commandLineParser    = languageServices.GetRequiredService <ICommandLineParserService>();
            var commandLineArguments = commandLineParser.Parse(commandLineArgs, projectDirectory, isInteractive: false, sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory());

            var metadataService = tmpWorkspace.Services.GetRequiredService <IMetadataService>();

            // we only support file paths in /r command line arguments
            var relativePathResolver =
                new RelativePathResolver(commandLineArguments.ReferencePaths, commandLineArguments.BaseDirectory);
            var commandLineMetadataReferenceResolver = new WorkspaceMetadataFileReferenceResolver(
                metadataService, relativePathResolver);

            var analyzerLoader     = tmpWorkspace.Services.GetRequiredService <IAnalyzerService>().GetLoader();
            var xmlFileResolver    = new XmlFileResolver(commandLineArguments.BaseDirectory);
            var strongNameProvider = new DesktopStrongNameProvider(commandLineArguments.KeyFileSearchPaths);

            // resolve all metadata references.
            var boundMetadataReferences      = commandLineArguments.ResolveMetadataReferences(commandLineMetadataReferenceResolver);
            var unresolvedMetadataReferences = boundMetadataReferences.FirstOrDefault(r => r is UnresolvedMetadataReference);

            if (unresolvedMetadataReferences != null)
            {
                throw new ArgumentException(string.Format(WorkspacesResources.Can_t_resolve_metadata_reference_colon_0, ((UnresolvedMetadataReference)unresolvedMetadataReferences).Reference));
            }

            // resolve all analyzer references.
            foreach (var path in commandLineArguments.AnalyzerReferences.Select(r => r.FilePath))
            {
                analyzerLoader.AddDependencyLocation(relativePathResolver.ResolvePath(path, baseFilePath: null));
            }

            var boundAnalyzerReferences      = commandLineArguments.ResolveAnalyzerReferences(analyzerLoader);
            var unresolvedAnalyzerReferences = boundAnalyzerReferences.FirstOrDefault(r => r is UnresolvedAnalyzerReference);

            if (unresolvedAnalyzerReferences != null)
            {
                throw new ArgumentException(string.Format(WorkspacesResources.Can_t_resolve_analyzer_reference_colon_0, ((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.An_error_occurred_while_reading_the_specified_configuration_file_colon_0, 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    = PathUtilities.GetRelativePath(projectDirectory, absolutePath);
                var isWithinProject = PathUtilities.IsChildPath(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    = PathUtilities.GetRelativePath(projectDirectory, absolutePath);
                var isWithinProject = PathUtilities.IsChildPath(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)
                // TODO (https://github.com/dotnet/roslyn/issues/4967):
                .WithMetadataReferenceResolver(new WorkspaceMetadataFileReferenceResolver(metadataService, new RelativePathResolver(ImmutableArray <string> .Empty, projectDirectory))),
                parseOptions: commandLineArguments.ParseOptions,
                documents: docs,
                additionalDocuments: additionalDocs,
                metadataReferences: boundMetadataReferences,
                analyzerReferences: boundAnalyzerReferences);

            return(projectInfo);
        }
        public static ProjectInfo CreateProjectInfo(string projectName, string language, IEnumerable <string> commandLineArgs, string projectDirectory, Workspace workspace = null)
#pragma warning restore RS0026 // Type is forwarded from MS.CA.Workspaces.Desktop.
        {
            // TODO (tomat): the method may throw all sorts of exceptions.
            var tmpWorkspace     = workspace ?? new AdhocWorkspace();
            var languageServices = tmpWorkspace.Services.GetLanguageServices(language);

            if (languageServices == null)
            {
                throw new ArgumentException(WorkspacesResources.Unrecognized_language_name);
            }

            var commandLineParser    = languageServices.GetRequiredService <ICommandLineParserService>();
            var commandLineArguments = commandLineParser.Parse(commandLineArgs, projectDirectory, isInteractive: false, sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory());

            var metadataService = tmpWorkspace.Services.GetRequiredService <IMetadataService>();

            // we only support file paths in /r command line arguments
            var relativePathResolver =
                new RelativePathResolver(commandLineArguments.ReferencePaths, commandLineArguments.BaseDirectory);
            var commandLineMetadataReferenceResolver = new WorkspaceMetadataFileReferenceResolver(
                metadataService, relativePathResolver);

            var analyzerLoader     = tmpWorkspace.Services.GetRequiredService <IAnalyzerService>().GetLoader();
            var xmlFileResolver    = new XmlFileResolver(commandLineArguments.BaseDirectory);
            var strongNameProvider = new DesktopStrongNameProvider(commandLineArguments.KeyFileSearchPaths);

            // Resolve all metadata references.
            //
            // In the command line compiler, it's entirely possible that duplicate reference paths may appear in this list; in the compiler
            // each MetadataReference object is a distinct instance, and the deduplication is ultimately performed in the ReferenceManager
            // once the Compilation actually starts to read metadata. In this code however,  we're resolving with the IMetadataService, which
            // has a default implementation to cache and return the same MetadataReference instance for a duplicate. This means duplicate
            // reference path will create duplicate MetadataReference objects, which is disallowed by ProjectInfo.Create -- even though the
            // compiler eventually would have dealt with it just fine. It's reasonable the Workspace APIs disallow duplicate reference objects
            // since it makes the semantics of APIs like Add/RemoveMetadataReference tricky. But since we want to not break for command lines
            // with duplicate references, we'll do a .Distinct() here, and let the Compilation do any further deduplication
            // that isn't handled by this explicit instance check. This does mean that the Compilations produced through this API
            // won't produce the "duplicate metadata reference" diagnostic like the real command line compiler would, but that's probably fine.
            //
            // Alternately, we could change the IMetadataService behavior to simply not cache, but that could theoretically break other
            // callers that would now see references across projects not be the same, or hurt performance for users of MSBuildWorkspace. Given
            // this is an edge case, it's not worth the larger fix here.
            var boundMetadataReferences      = commandLineArguments.ResolveMetadataReferences(commandLineMetadataReferenceResolver).Distinct().ToList();
            var unresolvedMetadataReferences = boundMetadataReferences.FirstOrDefault(r => r is UnresolvedMetadataReference);

            if (unresolvedMetadataReferences != null)
            {
                throw new ArgumentException(string.Format(WorkspacesResources.Can_t_resolve_metadata_reference_colon_0, ((UnresolvedMetadataReference)unresolvedMetadataReferences).Reference));
            }

            // resolve all analyzer references.
            foreach (var path in commandLineArguments.AnalyzerReferences.Select(r => r.FilePath))
            {
                analyzerLoader.AddDependencyLocation(relativePathResolver.ResolvePath(path, baseFilePath: null));
            }

            var boundAnalyzerReferences      = commandLineArguments.ResolveAnalyzerReferences(analyzerLoader);
            var unresolvedAnalyzerReferences = boundAnalyzerReferences.FirstOrDefault(r => r is UnresolvedAnalyzerReference);

            if (unresolvedAnalyzerReferences != null)
            {
                throw new ArgumentException(string.Format(WorkspacesResources.Can_t_resolve_analyzer_reference_colon_0, ((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.An_error_occurred_while_reading_the_specified_configuration_file_colon_0, 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    = PathUtilities.GetRelativePath(projectDirectory, absolutePath);
                var isWithinProject = PathUtilities.IsChildPath(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    = PathUtilities.GetRelativePath(projectDirectory, absolutePath);
                var isWithinProject = PathUtilities.IsChildPath(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>".
            var 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)
                // TODO (https://github.com/dotnet/roslyn/issues/4967):
                .WithMetadataReferenceResolver(new WorkspaceMetadataFileReferenceResolver(metadataService, new RelativePathResolver(ImmutableArray <string> .Empty, projectDirectory))),
                parseOptions: commandLineArguments.ParseOptions,
                documents: docs,
                additionalDocuments: additionalDocs,
                metadataReferences: boundMetadataReferences,
                analyzerReferences: boundAnalyzerReferences);

            return(projectInfo);
        }
        /// <summary>
        /// Adds a document to the workspace.
        /// </summary>
        public DocumentId AddDocument(ProjectId projectId, string name, string text)
        {
            if (projectId == null)
            {
                throw new ArgumentNullException("projectId");
            }

            if (name == null)
            {
                throw new ArgumentNullException("name");
            }

            if (text == null)
            {
                throw new ArgumentNullException("text");
            }

            var id = DocumentId.CreateNewId(projectId);

            this.AddDocument(
                DocumentInfo.Create(id, name, loader: TextLoader.From(TextAndVersion.Create(SourceText.From(text), VersionStamp.Create()))));

            return(id);
        }
        public static SourceGeneratedDocumentState Create(
            string hintName,
            SourceText generatedSourceText,
            SyntaxTree generatedSyntaxTree,
            DocumentId documentId,
            ISourceGenerator sourceGenerator,
            HostLanguageServices languageServices,
            SolutionServices solutionServices,
            CancellationToken cancellationToken
            )
        {
            var options  = generatedSyntaxTree.Options;
            var filePath = generatedSyntaxTree.FilePath;

            var textAndVersion = TextAndVersion.Create(generatedSourceText, VersionStamp.Create());
            ValueSource <TextAndVersion> textSource = new ConstantValueSource <TextAndVersion>(
                textAndVersion
                );

            var root = generatedSyntaxTree.GetRoot(cancellationToken);

            Contract.ThrowIfNull(
                languageServices.SyntaxTreeFactory,
                "We should not have a generated syntax tree for a language that doesn't support trees."
                );

            if (languageServices.SyntaxTreeFactory.CanCreateRecoverableTree(root))
            {
                // We will only create recoverable text if we can create a recoverable tree; if we created a
                // recoverable text but not a new tree, it would mean tree.GetText() could still potentially return
                // the non-recoverable text, but asking the document directly for it's text would give a recoverable
                // text with a different object identity.
                textSource = CreateRecoverableText(textAndVersion, solutionServices);

                generatedSyntaxTree = languageServices.SyntaxTreeFactory.CreateRecoverableTree(
                    documentId.ProjectId,
                    filePath: generatedSyntaxTree.FilePath,
                    options,
                    textSource,
                    generatedSourceText.Encoding,
                    root
                    );
            }

            var treeAndVersion = TreeAndVersion.Create(generatedSyntaxTree, textAndVersion.Version);

            return(new SourceGeneratedDocumentState(
                       languageServices,
                       solutionServices,
                       documentServiceProvider: null,
                       new DocumentInfo.DocumentAttributes(
                           documentId,
                           name: hintName,
                           folders: SpecializedCollections.EmptyReadOnlyList <string>(),
                           options.Kind,
                           filePath: filePath,
                           isGenerated: true,
                           designTimeOnly: false
                           ),
                       options,
                       sourceText: null, // don't strongly hold the text
                       textSource,
                       treeAndVersion,
                       sourceGenerator,
                       hintName
                       ));
        }