Beispiel #1
0
        static void GenerateCsx(string queryFilePath,
                                string packagesPath, QueryLanguage queryKind, string source,
                                IEnumerable <string> imports, IEnumerable <Reference> references,
                                IndentingLineWriter writer)
        {
            var body = queryKind == QueryLanguage.Expression
                     ? string.Join(Environment.NewLine, "System.Console.WriteLine(", source, ");")
                     : queryKind == QueryLanguage.Program
                     ? source + Environment.NewLine + "Main();"
                     : source;

            var rs = references.ToArray();

            File.WriteAllLines(Path.ChangeExtension(queryFilePath, ".csx"),
                               from lines in new[]
            {
                from r in rs
                select $"#r \"{r.Path}\"",

                Seq.Return(string.Empty),

                from ns in imports
                select $"using {ns};",

                Seq.Return(body, string.Empty),
            }
                               from line in lines
                               select line);

            // TODO User-supplied csi.cmd

            GenerateBatch(LoadTextResource("csi.cmd"), queryFilePath, packagesPath, rs);
        }
Beispiel #2
0
 static DataReceivedEventHandler OnProcessStdDataReceived(IndentingLineWriter writer) =>
 (_, e) =>
 {
     if (e.Data == null)
     {
         return;
     }
     writer?.WriteLines(e.Data);
 };
Beispiel #3
0
        static void GenerateExecutable(string cscPath, string queryFilePath,
                                       string packagesPath, QueryLanguage queryKind, string source,
                                       IEnumerable <string> imports, IEnumerable <Reference> references,
                                       IndentingLineWriter writer)
        {
            // TODO error handling in generated code

            var body =
                queryKind == QueryLanguage.Expression
                ? Seq.Return(
                    "static class UserQuery {",
                    "    static void Main() {",
                    "        System.Console.WriteLine(", source, ");",
                    "    }",
                    "}")
                : queryKind == QueryLanguage.Program
                ? Seq.Return(
                    "class UserQuery {",
                    "    static int Main(string[] args) {",
                    "        new UserQuery().Main(); return 0;",
                    "    }",
                    source,
                    "}")
                : Seq.Return(
                    "class UserQuery {",
                    "    static int Main(string[] args) {",
                    "        new UserQuery().Main(); return 0;",
                    "    }",
                    "    void Main() {",
                    source,
                    "    }",
                    "}");

            var rs = references.ToArray();

            var csFilePath = Path.ChangeExtension(queryFilePath, ".cs");

            File.WriteAllLines(csFilePath,
                               from lines in new[]
            {
                from ns in imports.GroupBy(e => e, StringComparer.Ordinal)
                select $"using {ns.First()};",

                body,

                Seq.Return(string.Empty),
            }
                               from line in lines
                               select line);

            var quoteOpt = QuoteOpt(' ');
            var args     = Seq.Return(csFilePath).Concat(rs.Select(r => "/r:" + r.Path))
                           .Select(quoteOpt);
            var argsLine = string.Join(" ", args);

            var workingDirPath = Path.GetDirectoryName(queryFilePath);

            if (cscPath != null)
            {
                if (!File.Exists(cscPath))
                {
                    throw new Exception("Invalid path to C# compiler binary: " + cscPath);
                }
            }
            else
            {
                var x86ProgramFilesPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
                cscPath =
                    Seq.Return("14.0", "12.0")
                    .Select(v => Path.Combine(x86ProgramFilesPath, "MSBuild", v, "bin", "csc.exe"))
                    .FirstOrDefault(File.Exists);

                if (cscPath == null)
                {
                    throw new Exception("Unable to find C# compiler in the expected location(s).");
                }
            }

            writer.WriteLine(quoteOpt(cscPath) + " " + argsLine);

            using (var process = Process.Start(new ProcessStartInfo
            {
                CreateNoWindow = true,
                UseShellExecute = false,
                FileName = cscPath,
                Arguments = argsLine,
                RedirectStandardError = true,
                RedirectStandardOutput = true,
                WorkingDirectory = string.IsNullOrEmpty(workingDirPath)
                                 ? Environment.CurrentDirectory
                                 : Path.GetFullPath(workingDirPath),
            }))
            {
                Debug.Assert(process != null);

                process.OutputDataReceived += OnProcessStdDataReceived(writer.Indent());
                process.ErrorDataReceived  += OnProcessStdDataReceived(writer.Indent());

                process.BeginOutputReadLine();
                process.BeginErrorReadLine();
                process.WaitForExit();

                var exitCode = process.ExitCode;
                if (exitCode != 0)
                {
                    throw new Exception($"C# compiler finished with a non-zero exit code of {exitCode}.");
                }
            }

            var queryDirPath = Path.GetFullPath(// ReSharper disable once AssignNullToNotNullAttribute
                Path.GetDirectoryName(queryFilePath))
                               + Path.DirectorySeparatorChar;

            var privatePaths =
                from r in rs
                select Path.Combine(queryDirPath, r.Path) into r
                    where File.Exists(r)
                select Path.GetDirectoryName(r) into d
                    where !string.IsNullOrEmpty(d)
                select MakeRelativePath(queryDirPath, d + Path.DirectorySeparatorChar) into d
                    where !Path.IsPathRooted(d)
                group 1 by d into g
                select g.Key.TrimEnd(PathSeparators);

            privatePaths = privatePaths.ToArray();

            if (privatePaths.Any())
            {
                var asmv1  = XNamespace.Get("urn:schemas-microsoft-com:asm.v1");
                var config =
                    new XElement("configuration",
                                 new XElement("runtime",
                                              new XElement(asmv1 + "assemblyBinding",
                                                           new XElement(asmv1 + "probing",
                                                                        new XAttribute("privatePath", string.Join(";", privatePaths))))));

                File.WriteAllText(Path.ChangeExtension(queryFilePath, ".exe.config"), config.ToString());
            }

            // TODO User-supplied csi.cmd

            GenerateBatch(LoadTextResource("exe.cmd"), queryFilePath, packagesPath, rs);
        }
Beispiel #4
0
        static IEnumerable <T> GetReferencesTree <T>(IPackageRepository repo,
                                                     IPackage package, FrameworkName targetFrameworkName, IndentingLineWriter writer,
                                                     Func <IPackageAssemblyReference, IPackage, T> selector)
        {
            writer?.WriteLine(package.GetFullName());

            IEnumerable <IPackageAssemblyReference> refs;

            if (VersionUtility.TryGetCompatibleItems(targetFrameworkName, package.AssemblyReferences, out refs))
            {
                foreach (var r in refs)
                {
                    yield return(selector(r, package));
                }
            }
            else
            {
                yield return(selector(null, package));
            }

            var subrefs =
                from d in package.GetCompatiblePackageDependencies(targetFrameworkName)
                select repo.FindPackage(d.Id) into dp
                    where dp != null
                from r in GetReferencesTree(repo, dp, targetFrameworkName,
                                            writer?.Indent(), selector)
                select r;

            foreach (var r in subrefs)
            {
                yield return(r);
            }
        }
Beispiel #5
0
        static T Compile <T>(string queryFilePath, IPackageRepository repo, string packagesPath,
                             IEnumerable <PackageReference> extraPackageReferences,
                             IEnumerable <string> extraImports,
                             FrameworkName targetFramework,
                             bool verbose, IndentingLineWriter writer,
                             Func <QueryLanguage, string, IEnumerable <string>, IEnumerable <Reference>, T> selector)
        {
            var eomLineNumber = LinqPad.GetEndOfMetaLineNumber(queryFilePath);
            var lines         = File.ReadLines(queryFilePath);

            var xml = string.Join(Environment.NewLine,
                                  // ReSharper disable once PossibleMultipleEnumeration
                                  lines.Take(eomLineNumber));

            var query = XElement.Parse(xml);

            if (verbose)
            {
                writer.Write(query);
            }

            QueryLanguage queryKind;

            if (!Enum.TryParse((string)query.Attribute("Kind"), true, out queryKind) ||
                (queryKind != QueryLanguage.Statements &&
                 queryKind != QueryLanguage.Expression &&
                 queryKind != QueryLanguage.Program))
            {
                throw new NotSupportedException("Only LINQPad " +
                                                "C# Statements and Expression queries are fully supported " +
                                                "and C# Program queries partially in this version.");
            }

            var nrs =
                from nrsq in new[]
            {
                from nr in query.Elements("NuGetReference")
                select new PackageReference((string)nr,
                                            SemanticVersion.ParseOptionalVersion((string)nr.Attribute("Version")),
                                            (bool?)nr.Attribute("Prerelease") ?? false),
                extraPackageReferences,
            }
            from nr in nrsq
                select new
            {
                nr.Id,
                nr.Version,
                nr.IsPrereleaseAllowed,
                Title = string.Join(" ", Seq.Return(nr.Id,
                                                    nr.Version?.ToString(),
                                                    nr.IsPrereleaseAllowed ? "(pre-release)" : null)
                                    .Filter()),
            };

            nrs = nrs.ToArray();

            if (verbose && nrs.Any())
            {
                writer.WriteLine($"Packages referenced ({nrs.Count():N0}):");
                writer.Indent().WriteLines(from nr in nrs select nr.Title);
            }

            writer.WriteLine($"Packages directory: {packagesPath}");
            var pm = new PackageManager(repo, packagesPath);

            pm.PackageInstalling += (_, ea) =>
                                    writer.WriteLine($"Installing {ea.Package}...");
            pm.PackageInstalled += (_, ea) =>
                                   writer.Indent().WriteLine($"Installed at {ea.InstallPath}");

            writer.WriteLine($"Packages target: {targetFramework}");

            var resolutionList = Enumerable.Repeat(new { Package      = default(IPackage),
                                                         AssemblyPath = default(string) }, 0)
                                 .ToList();

            foreach (var nr in nrs)
            {
                var pkg = pm.LocalRepository.FindPackage(nr.Id, nr.Version,
                                                         allowPrereleaseVersions: nr.IsPrereleaseAllowed,
                                                         allowUnlisted: false);
                if (pkg == null)
                {
                    pkg = repo.FindPackage(nr.Id, nr.Version,
                                           allowPrereleaseVersions: nr.IsPrereleaseAllowed,
                                           allowUnlisted: false);

                    if (pkg == null)
                    {
                        throw new Exception("Package not found: " + nr.Title);
                    }

                    pm.InstallPackage(pkg.Id, pkg.Version);
                }

                writer.WriteLine("Resolving references...");
                resolutionList.AddRange(GetReferencesTree(pm.LocalRepository, pkg, targetFramework, writer.Indent(), (r, p) => new
                {
                    Package      = p,
                    AssemblyPath = r != null
                                 ? Path.Combine(pm.PathResolver.GetInstallPath(p), r.Path)
                                 : null
                }));
            }

            var packagesPathWithTrailer = packagesPath + Path.DirectorySeparatorChar;

            var resolution =
                resolutionList
                .GroupBy(r => r.Package)
                .Select(g => g.First())
                .Select(r => new
            {
                r.Package,
                AssemblyPath = r.AssemblyPath != null
                            ? MakeRelativePath(queryFilePath, packagesPathWithTrailer)
                               + MakeRelativePath(packagesPathWithTrailer, r.AssemblyPath)
                            : null,
            })
                .Partition(r => r.AssemblyPath == null, (ok, nok) => new
            {
                ResolvedReferences    = ok,
                ReferencelessPackages = from r in nok
                                        select r.Package.GetFullName(),
            });

            resolution.ReferencelessPackages.StartIter(e =>
            {
                writer.WriteLine($"Warning! Packages with no references for {targetFramework}:");
                writer.Indent().WriteLines(e.ResumeFromCurrent());
            });

            var references = resolution.ResolvedReferences.ToArray();

            references.Select(r => r.AssemblyPath).StartIter(e =>
            {
                writer.WriteLine($"Resolved references ({references.Length:N0}):");
                writer.Indent().WriteLines(e.ResumeFromCurrent());
            });

            return
                (selector(
                     queryKind,
                     // ReSharper disable once PossibleMultipleEnumeration
                     string.Join(Environment.NewLine, lines.Skip(eomLineNumber)),
                     LinqPad.DefaultNamespaces
                     .Concat(from ns in query.Elements("Namespace")
                             select(string) ns)
                     .Concat(extraImports),
                     LinqPad.DefaultReferences.Select(r => new Reference(r))
                     .Concat(from r in query.Elements("Reference")
                             select(string) r into r
                             select r.StartsWith(LinqPad.RuntimeDirToken, StringComparison.OrdinalIgnoreCase)
                                        ? r.Substring(LinqPad.RuntimeDirToken.Length)
                                        : r into r
                             select new Reference(r))
                     .Concat(from r in references
                             select new Reference(r.AssemblyPath, r.Package))));
        }
Beispiel #6
0
        static Func <string, bool> Compiler(IPackageRepository repo, string packagesPath,
                                            IEnumerable <PackageReference> extraPackages,
                                            IEnumerable <string> extraImports,
                                            FrameworkName targetFramework,
                                            Generator generator,
                                            bool unlessUpToDate = false, bool force = false, bool verbose = false)
        {
            var writer = IndentingLineWriter.Create(Console.Out);

            return(queryFilePath =>
            {
                try
                {
                    var scriptFile = new FileInfo(Path.ChangeExtension(queryFilePath, ".csx"));
                    if (unlessUpToDate && scriptFile.Exists && scriptFile.LastWriteTime > File.GetLastWriteTime(queryFilePath))
                    {
                        if (verbose)
                        {
                            writer.WriteLine($"{queryFilePath}");
                            writer.Indent().WriteLine("Skipping compilation because target appears up to date.");
                        }
                        return false;
                    }

                    var packagesFullPath = Path.GetFullPath(Path.Combine(// ReSharper disable once AssignNullToNotNullAttribute
                                                                Path.GetDirectoryName(queryFilePath),
                                                                packagesPath));

                    writer.WriteLine($"{queryFilePath}");

                    var info = Compile(queryFilePath, repo, packagesFullPath,
                                       extraPackages, extraImports,
                                       targetFramework,
                                       verbose, writer.Indent(),
                                       (kind, src, imps, refs) => new
                    {
                        Kind = kind,
                        Source = src,
                        Imports = imps,
                        References = refs,
                    });

                    generator(queryFilePath, packagesFullPath,
                              info.Kind, info.Source, info.Imports,
                              info.References, writer.Indent());

                    return true;
                }
                catch (Exception e)
                {
                    if (!force)
                    {
                        throw;
                    }
                    writer.Indent().WriteLines($"WARNING! {e.Message}");
                    if (verbose)
                    {
                        writer.Indent().Indent().WriteLines(e.ToString());
                    }
                    return false;
                }
            });
        }