/* * Copyright 2013-2017 consulo.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package consulo.csharp.compiler; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.CompilerMessageCategory; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.Function; import consulo.csharp.module.extension.CSharpModuleExtension; import consulo.dotnet.DotNetTarget; import consulo.dotnet.compiler.DotNetCompileFailedException; import consulo.dotnet.compiler.DotNetCompilerMessage; import consulo.dotnet.compiler.DotNetCompilerOptionsBuilder; import consulo.dotnet.compiler.DotNetCompilerUtil; import consulo.dotnet.compiler.DotNetMacroUtil; import consulo.dotnet.module.extension.DotNetModuleExtension; /** * @author VISTALL * @since 26.11.13. */ public class MSBaseDotNetCompilerOptionsBuilder implements DotNetCompilerOptionsBuilder { private static final Logger LOGGER = Logger.getInstance(MSBaseDotNetCompilerOptionsBuilder.class); // impl from monolipse // monolipse.core/src/monolipse/core/runtime/CSharpCompilerLauncher.java // added support for column parsing by VISTALL private static Pattern LINE_ERROR_PATTERN = Pattern.compile("(.+)\\((\\d+),(\\d+)\\):\\s(error|warning) (\\w+\\d+):\\s(.+)"); @Nullable private String myExecutable; private final List<String> myArguments = new ArrayList<String>(); private final List<String> myProgramArguments = new ArrayList<String>(); public MSBaseDotNetCompilerOptionsBuilder addArgument(@NotNull String arg) { myArguments.add(arg + "\n"); return this; } public MSBaseDotNetCompilerOptionsBuilder addProgramArgument(@NotNull String arg) { myProgramArguments.add(arg); return this; } @Override public DotNetCompilerMessage convertToMessage(Module module, String line) { if(line.startsWith("error")) { return new DotNetCompilerMessage(CompilerMessageCategory.ERROR, line, null, -1, 1); } else { Matcher matcher = LINE_ERROR_PATTERN.matcher(line); if(matcher.matches()) { CompilerMessageCategory category = CompilerMessageCategory.INFORMATION; if(matcher.group(4).equals("error")) { category = CompilerMessageCategory.ERROR; } else if(matcher.group(4).equals("warning")) { category = CompilerMessageCategory.WARNING; } String fileUrl = FileUtil.toSystemIndependentName(matcher.group(1)); if(!FileUtil.isAbsolute(fileUrl)) { fileUrl = module.getModuleDirUrl() + "/" + fileUrl; } else { fileUrl = VirtualFileManager.constructUrl(StandardFileSystems.FILE_PROTOCOL, fileUrl); } int codeLine = Integer.parseInt(matcher.group(2)); int codeColumn = Integer.parseInt(matcher.group(3)); String message = matcher.group(6); if(ApplicationManager.getApplication().isInternal()) { message += "(" + matcher.group(5) + ")"; } return new DotNetCompilerMessage(category, message, fileUrl, codeLine, codeColumn); } } return null; } @Override @NotNull public GeneralCommandLine createCommandLine(@NotNull Module module, @NotNull VirtualFile[] results, @NotNull DotNetModuleExtension<?> extension) throws Exception { if(myExecutable == null) { throw new DotNetCompileFailedException("C# compiler is not found"); } CSharpModuleExtension csharpExtension = ModuleUtilCore.getExtension(module, CSharpModuleExtension.class); assert csharpExtension != null; String target = null; switch(extension.getTarget()) { case EXECUTABLE: target = "exe"; break; case WIN_EXECUTABLE: target = "winexe"; break; case LIBRARY: target = "library"; break; case NET_MODULE: target = "module"; break; } GeneralCommandLine commandLine = new GeneralCommandLine(); commandLine.setExePath(myExecutable); commandLine.setWorkDirectory(module.getModuleDirPath()); commandLine.addParameters(myProgramArguments); addArgument("/target:" + target); String outputFile = DotNetMacroUtil.expandOutputFile(extension); addArgument("/out:" + StringUtil.QUOTER.fun(outputFile)); Set<File> libraryFiles = DotNetCompilerUtil.collectDependencies(module, DotNetTarget.LIBRARY, false, DotNetCompilerUtil.ACCEPT_ALL); if(!libraryFiles.isEmpty()) { addArgument("/reference:" + StringUtil.join(libraryFiles, new Function<File, String>() { @Override public String fun(File file) { return StringUtil.QUOTER.fun(file.getAbsolutePath()); } }, ",")); } Set<File> moduleFiles = DotNetCompilerUtil.collectDependencies(module, DotNetTarget.NET_MODULE, false, DotNetCompilerUtil.ACCEPT_ALL); if(!moduleFiles.isEmpty()) { addArgument("/addmodule:" + StringUtil.join(moduleFiles, new Function<File, String>() { @Override public String fun(File file) { return StringUtil.QUOTER.fun(file.getAbsolutePath()); } }, ",")); } if(extension.isAllowDebugInfo()) { addArgument("/debug"); } if(csharpExtension.isAllowUnsafeCode()) { addArgument("/unsafe"); } if(csharpExtension.isOptimizeCode()) { addArgument("/optimize+"); } switch(csharpExtension.getPlatform()) { case ANY_CPU: addArgument("/platform:anycpu"); break; case ANY_CPU_32BIT_PREFERRED: addArgument("/platform:anycpu32bitpreferred"); break; case ARM: addArgument("/platform:ARM"); break; case X86: addArgument("/platform:x86"); break; case X64: addArgument("/platform:x64"); break; case ITANIUM: addArgument("/platform:Itanium"); break; } addArgument("/nologo"); addArgument("/nostdlib+"); String defineVariables = StringUtil.join(extension.getVariables(), ";"); if(!StringUtil.isEmpty(defineVariables)) { addArgument("/define:" + defineVariables); } String mainType = extension.getMainType(); if(!StringUtil.isEmpty(mainType)) { addArgument("/main:" + mainType); } for(VirtualFile result : results) { addArgument(StringUtil.QUOTER.fun(FileUtil.toSystemDependentName(result.getPath()))); } File tempFile = FileUtil.createTempFile("consulo-dotnet-rsp", ".rsp"); for(String argument : myArguments) { FileUtil.appendToFile(tempFile, argument); } //LOGGER.warn("Compiler def file: " + tempFile); //LOGGER.warn(FileUtil.loadFile(tempFile)); FileUtil.createParentDirs(new File(outputFile)); commandLine.addParameter("@" + tempFile.getAbsolutePath()); commandLine.setRedirectErrorStream(true); return commandLine; } @Nullable public String getExecutable() { return myExecutable; } public void setExecutable(@NotNull String executable) { myExecutable = executable; } public void setExecutableFromSdk(@NotNull Sdk sdk, @NotNull String executableFromSdk) { myExecutable = sdk.getHomePath() + File.separatorChar + executableFromSdk; } }