Beispiel #1
0
        /// <summary>
        /// Called before executing script f. If returns true, don't need to compile.
        /// </summary>
        /// <param name="f"></param>
        /// <param name="r">Receives file path and execution options.</param>
        /// <param name="projFolder">Project folder or null. If not null, f must be its main file.</param>
        public bool IsCompiled(FileNode f, out CompResults r, FileNode projFolder)
        {
            r = new CompResults();

            if (_data == null && !_Open())
            {
                return(false);
            }

            if (!_data.TryGetValue(f.Id, out string value))
            {
                return(false);
            }
            //Debug_.Print(value);
            int iPipe = 0;

            bool isScript = f.IsScript;

            r.role = MetaComments.DefaultRole(isScript);

            string asmFile;

            if (r.notInCache = value?.Starts("|=") ?? false)
            {
                iPipe = value.IndexOf('|', 2); if (iPipe < 0)
                {
                    iPipe = value.Length;
                }
                asmFile = value[2..iPipe];
Beispiel #2
0
        /// <summary>
        /// Compiles C# file or project if need.
        /// Returns false if fails (C# errors etc).
        /// </summary>
        /// <param name="reason">Whether to recompile if compiled, etc. See also Remarks.</param>
        /// <param name="r">Results.</param>
        /// <param name="f">C# file. If projFolder used, must be the main file of the project.</param>
        /// <param name="projFolder">null or project folder.</param>
        /// <remarks>
        /// Must be always called in the main UI thread (Thread.CurrentThread.ManagedThreadId == 1).
        ///
        /// Adds <see cref="MetaReferences.DefaultReferences"/>.
        ///
        /// If f role is classFile:
        ///		If CompReason.Run, does not compile (just parses meta), sets r.role=classFile and returns false.
        ///		Else compiles but does not create output files.
        /// </remarks>
        public static bool Compile(ECompReason reason, out CompResults r, FileNode f, FileNode projFolder = null)
        {
            Debug.Assert(Thread.CurrentThread.ManagedThreadId == 1);
            r = null;
            var  cache      = XCompiled.OfCollection(f.Model);
            bool isCompiled = reason != ECompReason.CompileAlways && cache.IsCompiled(f, out r, projFolder);

            //AOutput.Write("isCompiled=" + isCompiled);

            if (!isCompiled)
            {
                bool ok = false;
                try {
                    ok = _Compile(reason == ECompReason.Run, f, out r, projFolder);
                }
                catch (Exception ex) {
                    //AOutput.Write($"Failed to compile '{f.Name}'. {ex.ToStringWithoutStack()}");
                    AOutput.Write($"Failed to compile '{f.Name}'. {ex}");
                }

                if (!ok)
                {
                    cache.Remove(f, false);
                    return(false);
                }
            }

            return(true);
        }
Beispiel #3
0
        static bool _Compile(bool forRun, FileNode f, out CompResults r, FileNode projFolder)
        {
            var p1 = APerf.Create();

            r = new CompResults();

            var m = new MetaComments();

            if (!m.Parse(f, projFolder, EMPFlags.PrintErrors))
            {
                return(false);
            }
            var err = m.Errors;

            p1.Next('m');

            bool needOutputFiles = m.Role != ERole.classFile;

            //if for run, don't compile if f role is classFile
            if (forRun && !needOutputFiles)
            {
                r.role = ERole.classFile;
                return(false);
            }

            XCompiled cache = XCompiled.OfCollection(f.Model);
            string    outPath = null, outFile = null, fileName = null;

            if (needOutputFiles)
            {
                if (m.OutputPath != null)
                {
                    outPath  = m.OutputPath;
                    fileName = m.Name + ".dll";
                }
                else
                {
                    outPath  = cache.CacheDirectory;
                    fileName = f.IdString;
                }
                outFile = outPath + "\\" + fileName;
                AFile.CreateDirectory(outPath);
            }

            if (m.PreBuild.f != null && !_RunPrePostBuildScript(false, m, outFile))
            {
                return(false);
            }

            var po    = m.CreateParseOptions();
            var trees = new CSharpSyntaxTree[m.CodeFiles.Count];

            for (int i = 0; i < trees.Length; i++)
            {
                var f1 = m.CodeFiles[i];
                trees[i] = CSharpSyntaxTree.ParseText(f1.code, po, f1.f.FilePath, Encoding.UTF8) as CSharpSyntaxTree;
                //info: file path is used later in several places: in compilation error messages, run time stack traces (from PDB), Visual Studio debugger, etc.
                //	Our AOutputServer.SetNotifications callback will convert file/line info to links. It supports compilation errors and run time stack traces.
            }
            //p1.Next('t');

            string asmName = m.Name;

            if (m.Role == ERole.editorExtension)
            {
                asmName = asmName + "|" + (++c_versionCounter).ToString();                                             //AssemblyLoadContext.Default cannot load multiple assemblies with same name
            }
            var compilation = CSharpCompilation.Create(asmName, trees, m.References.Refs, m.CreateCompilationOptions());

            //p1.Next('c');

#if PDB
            string pdbFile = null;
#endif
            MemoryStream          pdbStream = null;
            string                xdFile    = null;
            Stream                xdStream  = null;
            Stream                resNat    = null;
            ResourceDescription[] resMan    = null;
            EmitOptions           eOpt      = null;

            if (needOutputFiles)
            {
                _AddAttributes(ref compilation, needVersionEtc: m.Role == ERole.miniProgram || m.Role == ERole.exeProgram);

                //create debug info always. It is used for run-time error links.
#if PDB
                pdbStream = new MemoryStream();
                if (m.OutputPath != null)
                {
                    pdbFile = Path.ChangeExtension(outFile, "pdb");
                    eOpt    = new EmitOptions(debugInformationFormat: DebugInformationFormat.Pdb, pdbFilePath: pdbFile);
                    //eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb, pdbFilePath: pdbFile); //smaller, but not all tools support it
                }
                else
                {
                    //eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.Embedded); //no, it is difficult to extract, because we load assembly from byte[] to avoid locking. We instead append portable PDB stream to the assembly stream.
                    eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb);
                    //adds < 1 KB; almost the same compiling speed. Separate pdb file is 14 KB; 2 times slower compiling, slower loading.
                }
#else
                if (m.OutputPath != null)
                {
                    //we don't use classic pdb file becouse of this error after switching to .NET Core:
                    //	Unexpected error writing debug information -- 'The version of Windows PDB writer is older than required: 'diasymreader.dll''
                    eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.Embedded);
                }
                else
                {
                    pdbStream = new MemoryStream();
                    //eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.Embedded); //no, it is difficult to extract, because we load assembly from byte[] to avoid locking. We instead append portable PDB stream to the assembly stream.
                    eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb);
                    //adds < 1 KB; almost the same compiling speed. Separate pdb file is 14 KB; 2 times slower compiling, slower loading.
                }
#endif

                if (m.XmlDocFile != null)
                {
                    xdStream = AFile.WaitIfLocked(() => File.Create(xdFile = APath.Normalize(m.XmlDocFile, outPath)));
                }

                resMan = _CreateManagedResources(m);
                if (err.ErrorCount != 0)
                {
                    err.PrintAll(); return(false);
                }

                if (m.Role == ERole.exeProgram || m.Role == ERole.classLibrary)
                {
                    resNat = _CreateNativeResources(m, compilation);
                }
                if (err.ErrorCount != 0)
                {
                    err.PrintAll(); return(false);
                }

                //EmbeddedText.FromX //it seems we can embed source code in PDB. Not tested.
            }

            //p1.Next();
            var asmStream  = new MemoryStream(8000);
            var emitResult = compilation.Emit(asmStream, pdbStream, xdStream, resNat, resMan, eOpt);

            if (needOutputFiles)
            {
                xdStream?.Dispose();
                resNat?.Dispose();                 //info: compiler disposes resMan
            }
            //p1.Next('e');

            var diag = emitResult.Diagnostics;
            if (!diag.IsEmpty)
            {
                foreach (var d in diag)
                {
                    if (d.Severity == DiagnosticSeverity.Hidden)
                    {
                        continue;
                    }
                    err.AddErrorOrWarning(d, f);
                    if (d.Severity == DiagnosticSeverity.Error && d.Id == "CS0009")
                    {
                        MetaReferences.RemoveBadReference(d.GetMessage());
                    }
                }
                err.PrintAll();
            }
            if (!emitResult.Success)
            {
                if (needOutputFiles)
                {
                    AFile.Delete(outFile);
#if PDB
                    if (pdbFile != null)
                    {
                        AFile.Delete(pdbFile);
                    }
#endif
                    if (xdFile != null)
                    {
                        AFile.Delete(xdFile);
                    }
                }
                return(false);
            }

            if (needOutputFiles)
            {
                //If there is no [STAThread], will need MTA thread.
                if (m.Role == ERole.miniProgram || m.Role == ERole.exeProgram)
                {
                    bool hasSTAThread = compilation.GetEntryPoint(default)?.GetAttributes().Any(o => o.ToString() == "System.STAThreadAttribute") ?? false;
Beispiel #4
0
            /// <summary>
            /// Called before executing script f. If returns true, don't need to compile.
            /// </summary>
            /// <param name="f"></param>
            /// <param name="r">Receives file path and execution options.</param>
            /// <param name="projFolder">Project folder or null. If not null, f must be its main file.</param>
            public bool IsCompiled(FileNode f, out CompResults r, FileNode projFolder)
            {
                r = new CompResults();

                if (_data == null && !_Open())
                {
                    return(false);
                }

                if (!_data.TryGetValue(f.Id, out string value))
                {
                    return(false);
                }
                //ADebug.Print(value);
                int iPipe = 0;

                bool isScript = f.IsScript;

                r.role = MetaComments.DefaultRole(isScript);

                string asmFile;

                if (r.notInCache = (value != null && value.Starts("|=")))
                {
                    iPipe = value.IndexOf('|', 2); if (iPipe < 0)
                    {
                        iPipe = value.Length;
                    }
                    asmFile = value.Substring(2, iPipe - 2);
                }
                else
                {
                    asmFile = CacheDirectory + "\\" + f.IdString;
                }
                //AOutput.Write(asmFile);

                if (!AFile.GetProperties(asmFile, out var asmProp, FAFlags.UseRawPath))
                {
                    return(false);
                }
                DateTime asmDate = asmProp.LastWriteTimeUtc;

                if (_IsFileModified(f))
                {
                    return(false);
                }

                bool isMultiFileProject = false;

                if (value != null && iPipe < value.Length)
                {
                    iPipe++;
                    foreach (var v in value.Segments("|", SegFlags.NoEmpty, iPipe..))
                    {
                        int  offs = v.start + 1;
                        char ch   = value[v.start];
                        switch (ch)
                        {
                        case 't':
                            r.role = (ERole)value.ToInt(offs);
                            break;

                        case 'a':
                            r.runMode = (ERunMode)value.ToInt(offs);
                            break;

                        case 'n':
                            r.ifRunning = (EIfRunning)value.ToInt(offs);
                            break;

                        case 'N':
                            r.ifRunning2 = (EIfRunning2)value.ToInt(offs);
                            break;

                        case 'u':
                            r.uac = (EUac)value.ToInt(offs);
                            break;

                        case 'b':
                            r.prefer32bit = true;
                            break;

                        case 'q':
                            r.console = true;
                            break;

                        case 'z':
                            r.mtaThread = true;
                            break;

                        case 'd':
                            r.pdbOffset = value.ToInt(offs);
                            break;

                        case 'p':
                            isMultiFileProject = true;
                            if (projFolder != null)
                            {
                                if (!Util.AHash.MD5Result.FromString(value.AsSpan(offs, v.end - offs), out var md5))
                                {
                                    return(false);
                                }
                                Util.AHash.MD5 md = default;
                                foreach (var f1 in projFolder.EnumProjectClassFiles(f))
                                {
                                    if (_IsFileModified(f1))
                                    {
                                        return(false);
                                    }
                                    md.Add(f1.Id);
                                }
                                if (md.IsEmpty || md.Hash != md5)
                                {
                                    return(false);
                                }
                            }
                            break;

                        case '*':
                            var dll = value[offs..v.end];