This is an automated email from the ASF dual-hosted git repository. vieiro pushed a commit to branch cnd in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/cnd by this push: new ae52a9b [cnd] 3.1 makeproject compile_commands.json new 8115eec Merge pull request #3553 from vieiro/feature/cnd-lsp ae52a9b is described below commit ae52a9b9ce28984a9fa7f223f79193c76966d78f Author: Antonio Vieiro <vie...@apache.org> AuthorDate: Tue Feb 1 21:39:38 2022 +0100 [cnd] 3.1 makeproject compile_commands.json Automatically generate compile_commands.json for make based projects. --- cnd/cnd.api.project/nbproject/project.xml | 3 +- cnd/cnd.lsp/build.xml | 25 ++ cnd/cnd.lsp/manifest.mf | 6 + cnd/cnd.lsp/nbproject/project.properties | 20 ++ cnd/cnd.lsp/nbproject/project.xml | 141 +++++++++ .../lsp/compilationdb/ClangCDBGenerationCause.java | 53 ++++ .../lsp/compilationdb/ClangCDBGenerationTask.java | 341 +++++++++++++++++++++ .../cnd/lsp/compilationdb/ClangCDBSupport.java | 219 +++++++++++++ .../lsp/compilationdb/CommandObjectBuilder.java | 131 ++++++++ .../makeproject/LSPMakeProjectLookupProvider.java | 40 +++ .../modules/cnd/lsp/resources/Bundle.properties | 22 ++ cnd/cnd.makeproject/nbproject/project.xml | 1 + cnd/cnd.toolchain/nbproject/project.xml | 3 +- ide/dlight.nativeexecution/nbproject/project.xml | 1 + nbbuild/cluster.properties | 1 + 15 files changed, 1005 insertions(+), 2 deletions(-) diff --git a/cnd/cnd.api.project/nbproject/project.xml b/cnd/cnd.api.project/nbproject/project.xml index 0d1cdd8..c78efff 100644 --- a/cnd/cnd.api.project/nbproject/project.xml +++ b/cnd/cnd.api.project/nbproject/project.xml @@ -111,7 +111,6 @@ <friend>org.netbeans.modules.cnd.analysis.impl</friend> <friend>org.netbeans.modules.cnd.apt</friend> <friend>org.netbeans.modules.cnd.audit</friend> - <friend>org.netbeans.modules.cnd.jconvertwizard</friend> <friend>org.netbeans.modules.cnd.classview</friend> <friend>org.netbeans.modules.cnd.codemodel.bridge</friend> <friend>org.netbeans.modules.cnd.codemodel.utils</friend> @@ -122,6 +121,8 @@ <friend>org.netbeans.modules.cnd.gizmo</friend> <friend>org.netbeans.modules.cnd.gotodeclaration</friend> <friend>org.netbeans.modules.cnd.highlight</friend> + <friend>org.netbeans.modules.cnd.jconvertwizard</friend> + <friend>org.netbeans.modules.cnd.lsp</friend> <friend>org.netbeans.modules.cnd.makeproject</friend> <friend>org.netbeans.modules.cnd.makeproject.source.bridge</friend> <friend>org.netbeans.modules.cnd.makeproject.ui</friend> diff --git a/cnd/cnd.lsp/build.xml b/cnd/cnd.lsp/build.xml new file mode 100644 index 0000000..9f9e3c3 --- /dev/null +++ b/cnd/cnd.lsp/build.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + +--> +<project basedir="." default="netbeans" name="cnd/cnd.lsp"> + <description>Builds, tests, and runs the project org.netbeans.modules.cnd.lsp</description> + <import file="../../nbbuild/templates/projectized.xml"/> +</project> diff --git a/cnd/cnd.lsp/manifest.mf b/cnd/cnd.lsp/manifest.mf new file mode 100644 index 0000000..ad635bf --- /dev/null +++ b/cnd/cnd.lsp/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +AutoUpdate-Show-In-Client: false +OpenIDE-Module: org.netbeans.modules.cnd.lsp +OpenIDE-Module-Implementation-Version: 1 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/cnd/lsp/resources/Bundle.properties + diff --git a/cnd/cnd.lsp/nbproject/project.properties b/cnd/cnd.lsp/nbproject/project.properties new file mode 100644 index 0000000..355b38a --- /dev/null +++ b/cnd/cnd.lsp/nbproject/project.properties @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +is.eager=true +javac.source=1.8 +javac.compilerargs=-Xlint -Xlint:-serial +spec.version.base=1.0.0 diff --git a/cnd/cnd.lsp/nbproject/project.xml b/cnd/cnd.lsp/nbproject/project.xml new file mode 100644 index 0000000..adfac30 --- /dev/null +++ b/cnd/cnd.lsp/nbproject/project.xml @@ -0,0 +1,141 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + +--> +<project xmlns="http://www.netbeans.org/ns/project/1"> + <type>org.netbeans.modules.apisupport.project</type> + <configuration> + <data xmlns="http://www.netbeans.org/ns/nb-module-project/3"> + <code-name-base>org.netbeans.modules.cnd.lsp</code-name-base> + <module-dependencies> + <dependency> + <code-name-base>org.netbeans.libs.json_simple</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <release-version>1</release-version> + <specification-version>0.27</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.netbeans.modules.cnd.api.project</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <release-version>1</release-version> + <specification-version>1.37.9</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.netbeans.modules.cnd.makeproject</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <release-version>2</release-version> + <specification-version>2.59.20</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.netbeans.modules.cnd.makeproject.ui</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <specification-version>1.1.8</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.netbeans.modules.cnd.toolchain</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <specification-version>1.35.14</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.netbeans.modules.dlight.nativeexecution</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <specification-version>1.54</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.netbeans.modules.projectapi</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <release-version>1</release-version> + <specification-version>1.85</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.netbeans.modules.projectuiapi.base</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <release-version>1</release-version> + <specification-version>1.101</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.openide.awt</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <specification-version>7.83</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.openide.filesystems</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <specification-version>9.27</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.openide.nodes</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <specification-version>7.60</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.openide.util</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <specification-version>9.23</specification-version> + </run-dependency> + </dependency> + <dependency> + <code-name-base>org.openide.util.lookup</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <specification-version>8.49</specification-version> + </run-dependency> + </dependency> + </module-dependencies> + <public-packages/> + </data> + </configuration> +</project> diff --git a/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/ClangCDBGenerationCause.java b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/ClangCDBGenerationCause.java new file mode 100644 index 0000000..fa963e2 --- /dev/null +++ b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/ClangCDBGenerationCause.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.netbeans.modules.cnd.lsp.compilationdb; + +/** + * ClangCDBGenerationCause represents possible causes that require a Clang + * compilation database to be regenerated. + * + * @author antonio + */ +enum ClangCDBGenerationCause { + /** + * Some files were added to the project + */ + FILES_ADDED, + /** + * Some files were removed from the project + */ + FILES_REMOVED, + /** + * Some files were renamed in the project + */ + FILES_RENAMED, + /** + * Project configuration changed + */ + PROJECT_CONFIGURATION_CHANGED, + /** + * Project is opened. + */ + PROJECT_OPENED, + /** + * Include paths or macro defintions have changed for + * some files or for the whole project. + */ + INCLUDES_MACROS_CHANGED +} diff --git a/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/ClangCDBGenerationTask.java b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/ClangCDBGenerationTask.java new file mode 100644 index 0000000..04739cc --- /dev/null +++ b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/ClangCDBGenerationTask.java @@ -0,0 +1,341 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.netbeans.modules.cnd.lsp.compilationdb; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.json.simple.JSONObject; +import org.netbeans.modules.cnd.api.project.NativeFileItem.Language; +import static org.netbeans.modules.cnd.api.project.NativeFileItem.Language.CPP; +import static org.netbeans.modules.cnd.api.project.NativeFileItem.Language.C_HEADER; +import static org.netbeans.modules.cnd.api.project.NativeFileItem.Language.FORTRAN; +import static org.netbeans.modules.cnd.api.project.NativeFileItem.Language.OTHER; +import org.netbeans.modules.cnd.api.project.NativeProject; +import org.netbeans.modules.cnd.api.toolchain.AbstractCompiler; +import org.netbeans.modules.cnd.api.toolchain.CompilerSet; +import org.netbeans.modules.cnd.api.toolchain.PredefinedToolKind; +import org.netbeans.modules.cnd.api.toolchain.Tool; +import org.netbeans.modules.cnd.makeproject.api.MakeProject; +import org.netbeans.modules.cnd.makeproject.api.configurations.BasicCompilerConfiguration; +import org.netbeans.modules.cnd.makeproject.api.configurations.CCCompilerConfiguration; +import org.netbeans.modules.cnd.makeproject.api.configurations.CCompilerConfiguration; +import org.netbeans.modules.cnd.makeproject.api.configurations.ConfigurationDescriptorProvider; +import org.netbeans.modules.cnd.makeproject.api.configurations.Item; +import org.netbeans.modules.cnd.makeproject.api.configurations.ItemConfiguration; +import org.netbeans.modules.cnd.makeproject.api.configurations.MakeConfiguration; +import org.netbeans.modules.cnd.makeproject.api.configurations.MakeConfigurationDescriptor; +import org.openide.filesystems.FileUtil; +import org.openide.util.Cancellable; + +/** + * ClangCDBGenerationTask creates/updates a Clang compilation database as + * defined https://clang.llvm.org/docs/JSONCompilationDatabase.html + * + * @author antonio + */ +final class ClangCDBGenerationTask implements Callable<Void>, Cancellable { + + private static final String CDB_NAME = "compile_commands.json"; // NOI18N + private static final Logger LOG + = Logger.getLogger(ClangCDBGenerationTask.class.getName()); + + protected boolean cancelled; + private final ClangCDBSupport support; + private final BlockingQueue<ClangCDBGenerationCause> pendingCauses; + private final MakeProject makeProject; + private final NativeProject nativeProject; + private final MakeConfiguration activeMakeConfiguration; + private final CompilerSet compilerSet; + private final ConfigurationDescriptorProvider configurationDescriptorProvider; + private final MakeConfigurationDescriptor makeConfigurationDescriptor; + + ClangCDBGenerationTask(ClangCDBSupport support, BlockingQueue<ClangCDBGenerationCause> pendingCauses) + throws IllegalArgumentException { + this.support = support; + this.makeProject = support.getMakeProject(); + this.pendingCauses = pendingCauses; + this.cancelled = false; + + // Prepare some required objects + nativeProject = makeProject.getLookup().lookup(NativeProject.class); + if (nativeProject == null) { + throw new IllegalArgumentException("No native project found"); + } + + if (makeProject.getDevelopmentHost() != null && !makeProject.getDevelopmentHost().isLocal()) { + throw new IllegalArgumentException("Not a local project"); + } + + activeMakeConfiguration = makeProject.getActiveConfiguration(); + if (activeMakeConfiguration == null) { + throw new IllegalArgumentException("No active make configuration"); + } + + compilerSet = activeMakeConfiguration.getCompilerSet().getCompilerSet(); + if (compilerSet == null) { + throw new IllegalArgumentException("No compilerset defined"); + } + + configurationDescriptorProvider + = makeProject.getLookup().lookup(ConfigurationDescriptorProvider.class); + if (configurationDescriptorProvider == null) { + throw new IllegalArgumentException("No configuration descriptor found"); + } + + makeConfigurationDescriptor = configurationDescriptorProvider.getConfigurationDescriptor(); + if (makeConfigurationDescriptor == null) { + throw new IllegalArgumentException("No MakeConfigurationDescriptor"); + } + + } + + @Override + public Void call() throws Exception { + + try { + updateCompilationDatabase(); + } catch (Exception e) { + LOG.log(Level.SEVERE, String.format("Error updating compilation database:%s:%s", e.getMessage(), e.getClass().getName()), e); + throw e; + } + return null; + } + + public void updateCompilationDatabase() throws Exception { + LOG.log(Level.FINE, "Updating compilation database"); + + if (cancelled) { + LOG.log(Level.FINE, "Task was cancelled."); + return; + } + + if (!support.isOpen()) { + LOG.log(Level.FINE, "Project is not open, bailing out."); + return; + } + + if (pendingCauses.isEmpty()) { + LOG.log(Level.FINE, "No pending causes, bailing out"); + return; + } + + ArrayList<ClangCDBGenerationCause> coalescedCauses = new ArrayList<>(pendingCauses.size()); + pendingCauses.drainTo(coalescedCauses); + + File projectDirectory = FileUtil.toFile(makeProject.getProjectDirectory()); + File compilationDatabase = new File(projectDirectory, CDB_NAME); + LOG.log(Level.FINE, "Updating compilation database {0} because {1}", + new Object[]{compilationDatabase.getPath(), coalescedCauses.toString()}); + + // Get the project items, + Item[] items = makeConfigurationDescriptor.getProjectItems(); + if (items == null || items.length == 0) { + LOG.log(Level.FINE, "No items in project."); + return; + } + + if (cancelled) { + LOG.log(Level.FINE, "Task cancelled"); + return; + } + + long startTime = System.currentTimeMillis(); + + // Use a temporary file to create the compilation database. + File tempFile = Files.createTempFile("CDB", ".json").toFile(); // NOI18N + + try ( FileOutputStream outputStream = new FileOutputStream(tempFile); // + PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) { + FileChannel channel = outputStream.getChannel(); + FileLock lock = channel.lock(); + writeCompilationDatabase(writer, lock, items); + } + + // Only update the compilation database if we're done with all items. + if (!cancelled) { + try { + Files.move(tempFile.toPath(), compilationDatabase.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (Throwable e) { + LOG.log(Level.SEVERE, String.format("Error updating compilation database %s:%s", e.getMessage(), e.getClass().getName()), e); + } + } + + // And delete the temporary file + tempFile.delete(); + + long endTime = System.currentTimeMillis(); + LOG.log(Level.INFO, "Regeneration of {0} took {1} ms.", new Object[]{ + compilationDatabase.getAbsolutePath(), + endTime - startTime + }); + } + + private void writeCompilationDatabase(PrintWriter writer, FileLock lock, Item[] items) + throws Exception { + + // We don't want to hold all command objects in memory, but write + // them sequentially. + writer.println("["); + boolean firstItem = true; + for (Item item : items) { + if (cancelled) { + return; + } + LOG.log(Level.FINE, "Updating compilation db for item {0}", item.getName()); + Language language = item.getLanguage(); + ItemConfiguration itemConfiguration = item.getItemConfiguration(activeMakeConfiguration); + JSONObject commandObject = null; + switch (language) { + case C_HEADER: + // C headers are not included in compilation databases + continue; + case C: + case CPP: + case OTHER: // Assembler? + // C and C++ files (and possibly assembler) are included in the compilation database + commandObject = getCommandObjectForItem( + language, item, itemConfiguration); + break; + case FORTRAN: + // Fortran is not supported in clang-style compilation databases, AFAIK + break; + } + if (commandObject != null) { + if (firstItem) { + firstItem = false; + } else { + writer.print(","); + } + commandObject.writeJSONString(writer); + writer.println(); + } + } + writer.println("]"); + } + + /** + * Returns a Command Object required to compile an item. + * + * @param makeProject The project + * @param activeMakeConfiguration The active make configuration. + * @param compilerSet The compiler set. + * @param item The item being compiled. + * @param itemConfiguration The item configuration of the item. + * @return A JSON String with this command object. + * @throws Exception If an I/O error happens. + */ + private JSONObject getCommandObjectForItem( + Language language, Item item, ItemConfiguration itemConfiguration) + throws Exception { + + if (itemConfiguration.getExcluded().getValue()) { + LOG.log(Level.FINE, "Skipping excluded item {0}", + item.getName()); + return null; + } + + CommandObjectBuilder builder = new CommandObjectBuilder(makeProject); + + // 0. 'directory' entry in the command object is automatically added. + // 1. 'file' entry in the command object. + LOG.log(Level.FINE, "Creating command object for item: {0} in file: {1}", new Object[]{item.getName(), item.getAbsolutePath()}); + builder.setFile(item.getAbsolutePath()); + + // 2. 'command' entry in the command object + PredefinedToolKind toolKind = itemConfiguration.getTool(); + Tool compilerTool = compilerSet.getTool(toolKind); + if (!(compilerTool instanceof AbstractCompiler)) { + LOG.log(Level.FINE, "Cannot find an AbstractCompiler for item {0}", + item.getName()); + return null; + } + AbstractCompiler compiler = (AbstractCompiler) compilerTool; + // 2.1 compiler path ("/usr/bin/gcc", for instance). + builder.addCommandItem(compiler.getPath()); + + // 2.2 "-c" flag. This is currently hardcoded in NetBeans CND... :-( + builder.addCommandItem("-c"); + + // 2.3 The full path of the file to compile ("/home/users/user/project/myfile.c") + builder.addCommandItem(builder.getFile()); + + // 2.3 language specific flags + // C Options + { + CCompilerConfiguration cConfiguration = itemConfiguration.getCCompilerConfiguration(); + if (cConfiguration != null) { + String cFlags = cConfiguration.getCFlags(compiler); + builder.addCommandItem(cFlags); + String allOptions = cConfiguration.getAllOptions2(compiler); + builder.addCommandItem(allOptions); + LOG.log(Level.FINE, "C: CFLAGS {0} options {1}", new Object[]{cFlags, allOptions}); + } + } + + // C++ options + { + CCCompilerConfiguration cppConfiguration = itemConfiguration.getCCCompilerConfiguration(); + if (cppConfiguration != null) { + String cppFlags = cppConfiguration.getCCFlags(compiler); + builder.addCommandItem(cppFlags); + String allOptions = cppConfiguration.getAllOptions2(compiler); + builder.addCommandItem(allOptions); + LOG.log(Level.FINE, "C++: CFLAGS {0} options {1}", new Object[]{cppFlags, allOptions}); + } + } + + // 3. output file ("/home/users/user/project/build/Linux/X86/myfile.o", for instance) + // Note that "output" is optional in CommandObjects + BasicCompilerConfiguration basicCompilerConfiguration = itemConfiguration.getCompilerConfiguration(); + String outputFile = basicCompilerConfiguration.getOutputFile(item, activeMakeConfiguration, true); + + if (outputFile != null) { + // outputFile is constructed with Makefile macros, like these: + // "/home/antonio/tmp/${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}/main.o + // We want to expand these + outputFile = activeMakeConfiguration.expandMacros(outputFile); + builder.setOutput(outputFile); + + // We also include a '-o outputfile' to the command + builder.addCommandItem("-o"); + builder.addCommandItem(outputFile); + } + + return builder.build(); + } + + @Override + public boolean cancel() { + this.cancelled = true; + return true; + } + +} diff --git a/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/ClangCDBSupport.java b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/ClangCDBSupport.java new file mode 100644 index 0000000..e7ca389 --- /dev/null +++ b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/ClangCDBSupport.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.netbeans.modules.cnd.lsp.compilationdb; + +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.modules.cnd.api.project.NativeFileItem; +import org.netbeans.modules.cnd.api.project.NativeProject; +import org.netbeans.modules.cnd.api.project.NativeProjectItemsListener; +import org.netbeans.modules.cnd.makeproject.api.MakeProject; +import org.netbeans.modules.cnd.makeproject.api.support.MakeProjectEvent; +import org.netbeans.modules.cnd.makeproject.api.support.MakeProjectListener; +import org.netbeans.spi.project.ui.ProjectOpenedHook; +import org.openide.util.RequestProcessor; + +/** + * ClangCDBSupport is responsible for detecting and generating a JSON + * compilation database, as defined in + * https://clang.llvm.org/docs/JSONCompilationDatabase.html + * + * When "ProjectOpenedHook.projectOpened" is fired, i.e., when the project is + * opened, we start listening to NativeProjectItemsListener and + * MakeProjectListener. + * + * We remove these listeners when the project is closed or deleted. + * + * The compilation database is regenerated after COALESCING_DELAY_MS + * milliseconds. Requests are coalesced using a "pendingCauses" blocking queue. + */ +public final class ClangCDBSupport + extends ProjectOpenedHook + implements + NativeProjectItemsListener, MakeProjectListener { + + private static final Logger LOG = Logger.getLogger(ClangCDBSupport.class.getName()); + private static final RequestProcessor CLANG_CDB_PROCESSOR = new RequestProcessor(ClangCDBSupport.class.getName()); + private static final long COALESCING_DELAY_MS = 1000; + private final MakeProject makeProject; + private final BlockingQueue<ClangCDBGenerationCause> pendingCauses; + private boolean projectOpen; + + public ClangCDBSupport(final MakeProject makeProject) { + this.makeProject = makeProject; + this.projectOpen = false; + this.pendingCauses = new LinkedBlockingQueue<>(); + } + + /** + * Called when multiple files are added to the project. + * + * @param fileItems the list of file items that was added. + */ + @Override + public void filesAdded(List<NativeFileItem> fileItems) { + updateCompilationDatabase(ClangCDBGenerationCause.FILES_ADDED); + } + + /** + * Called when multiple files are removed from the project. + * + * @param fileItems the list of file items that was added. + */ + @Override + public void filesRemoved(List<NativeFileItem> fileItems) { + updateCompilationDatabase(ClangCDBGenerationCause.FILES_REMOVED); + } + + /** + * Called when include paths or macro definitions have changed (and files + * needs to be re-parsed) for multiple files. + * + * @param fileItems the list of file items that has changed. + */ + @Override + public void filesPropertiesChanged(List<NativeFileItem> fileItems) { + updateCompilationDatabase(ClangCDBGenerationCause.INCLUDES_MACROS_CHANGED); + } + + /** + * Called when include paths or macro definitions have changed (and files + * needs to be re-parsed) for all files in project. + * + * @param nativeProject project whose properties have changed + */ + @Override + public void filesPropertiesChanged(NativeProject nativeProject) { + updateCompilationDatabase(ClangCDBGenerationCause.INCLUDES_MACROS_CHANGED); + } + + @Override + public void fileRenamed(String oldPath, NativeFileItem newFileIetm) { + updateCompilationDatabase(ClangCDBGenerationCause.FILES_RENAMED); + } + + /** + * notifies about intensive file operations to be started. We ignore these. + * + * @param nativeProject + */ + @Override + public void fileOperationsStarted(NativeProject nativeProject) { + LOG.log(Level.FINE, "File operations started."); + } + + /** + * notifies about intensive file operations finished. We ignore these + * + * @param nativeProject + */ + @Override + public void fileOperationsFinished(NativeProject nativeProject) { + LOG.log(Level.FINE, "File operations finished."); + } + + @Override + public void configurationXmlChanged(MakeProjectEvent ev) { + updateCompilationDatabase(ClangCDBGenerationCause.PROJECT_CONFIGURATION_CHANGED); + } + + /** + * Ignored. See javadoc + * @param ev + */ + @Override + public void propertiesChanged(MakeProjectEvent ev) { + LOG.log(Level.FINE, "Some project properties changed {0}", + ev.getPath()); + } + + @Override + protected void projectOpened() { + LOG.log(Level.FINE, "Project opened"); + projectOpen = true; + makeProject.getHelper().addMakeProjectListener(this); + NativeProject nativeProject = makeProject.getLookup().lookup(NativeProject.class); + if (nativeProject != null) { + nativeProject.addProjectItemsListener(this); + } else { + LOG.log(Level.FINE, "Cannot attach to native project events."); + } + updateCompilationDatabase(ClangCDBGenerationCause.PROJECT_OPENED); + } + + @Override + protected void projectClosed() { + LOG.log(Level.FINE, "Project closed"); + removeListenersOnCloseOrDelete(); + } + + @Override + public void projectDeleted(NativeProject nativeProject) { + LOG.log(Level.FINE, "Project deleted"); + removeListenersOnCloseOrDelete(); + } + + private void removeListenersOnCloseOrDelete() { + projectOpen = false; + makeProject.getHelper().removeMakeProjectListener(this); + NativeProject nativeProject = makeProject.getLookup().lookup(NativeProject.class); + if (nativeProject != null) { + nativeProject.removeProjectItemsListener(this); + } else { + LOG.log(Level.FINE, "Cannot detach from native project events."); + } + } + + /** + * Updates the compilation database "[project]/compile_commands.json". If a + * task is already running then the request is silently ignored. + * + * @param cause The cause that originated the need to recreate the + * compilation database. + */ + private void updateCompilationDatabase(ClangCDBGenerationCause cause) { + LOG.log(Level.FINE, "Update compilation database project is open: {0}", projectOpen); + if (projectOpen) { + pendingCauses.add(cause); + ClangCDBGenerationTask task = null; + try { + task = new ClangCDBGenerationTask(this, pendingCauses); + } catch (IllegalArgumentException iae) { + LOG.log(Level.SEVERE, "Unable to create ClangCDBGenerationTask: {0}", iae.getMessage()); + return; + } + // We fire the regeneration task after a COALESCING_DELAY_MS milliseconds, to + // coalesce requests (for instance, when many files are added or removed). + CLANG_CDB_PROCESSOR.schedule(task, COALESCING_DELAY_MS, TimeUnit.MILLISECONDS); + } + } + + MakeProject getMakeProject() { + return makeProject; + } + + boolean isOpen() { + return projectOpen; + } + +} diff --git a/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/CommandObjectBuilder.java b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/CommandObjectBuilder.java new file mode 100644 index 0000000..10d0a4d --- /dev/null +++ b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/compilationdb/CommandObjectBuilder.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.netbeans.modules.cnd.lsp.compilationdb; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.json.simple.JSONObject; +import org.netbeans.api.project.Project; +import org.openide.filesystems.FileObject; + +/** + * CommandObjectBuilder builds a JSONObject representing a CommandObject as + * defined in https://clang.llvm.org/docs/JSONCompilationDatabase.html + */ +final class CommandObjectBuilder { + + private static final Logger LOG = Logger.getLogger(CommandObjectBuilder.class.getName()); + + private final Project project; + private final StringBuilder command; + private String file; + private String output; + + CommandObjectBuilder(Project project) { + this.project = project; + this.command = new StringBuilder(); + } + + /** + * Appends another piece of the 'command' entry in the command object. For + * instance, you can add "-strip" or "-std=ansi". + * + * @param part The part to append at the end of the command line. + * @return this + */ + CommandObjectBuilder addCommandItem(String part) { + if (part != null) { + command.append(' ').append(part).append(' '); + } + return this; + } + + /** + * Sets the 'file' entry in the command object. For instance, + * "/usr/home/user/project/folder/file.c" + * + * @param file The file. Absolute paths are recommended, but can be relative + * to the project.getProjectDirectory(). + * @return this + */ + CommandObjectBuilder setFile(String file) { + this.file = Paths.get(file).toAbsolutePath().toString(); + return this; + } + + /** + * Sets the 'output' entry in the command object. For instance, + * "/usr/home/user/project/build/platform/file.o" + * + * @param output The file. Absolute paths are recommended, but can be + * relative to the project.getProjectDirectory(). + * @return this. + */ + CommandObjectBuilder setOutput(String output) { + this.output = Paths.get(output).toAbsolutePath().toString(); + return this; + } + + String getFile() { + return file; + } + + /** + * Builds a JSONObject representing this command object. NOTE: 'directory' + * entry is automatically set to project.getProjectDirectory(). + * + * @return The JSONObject representing this command object. + * @throws IllegalStateException if a mandatory entri in the command object + * is not present. + */ + JSONObject build() throws IllegalStateException { + if (file == null) { + throw new IllegalStateException("Missing file"); + } + if (command.length() == 0) { + throw new IllegalStateException("No command"); + } + + JSONObject object = new JSONObject(); + + // directory + FileObject projectDirectory = project.getProjectDirectory(); + Path directory = Paths.get(projectDirectory.toURI()); + directory = directory.toAbsolutePath(); + LOG.log(Level.FINE, "Directory {0}", directory.toString()); + object.put("directory", directory.toString()); + + // file + LOG.log(Level.FINE, "File {0}", file); + object.put("file", file); + + // command + object.put("command", command.toString().trim()); + LOG.log(Level.FINE, "Command {0}", command.toString()); + + // output (this one is optional) + if (output != null) { + object.put("output", output); + } + return object; + } + +} diff --git a/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/makeproject/LSPMakeProjectLookupProvider.java b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/makeproject/LSPMakeProjectLookupProvider.java new file mode 100644 index 0000000..0913516 --- /dev/null +++ b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/makeproject/LSPMakeProjectLookupProvider.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.netbeans.modules.cnd.lsp.makeproject; + +import java.util.ArrayList; +import org.netbeans.modules.cnd.lsp.compilationdb.ClangCDBSupport; +import org.netbeans.modules.cnd.makeproject.api.MakeProject; +import org.netbeans.modules.cnd.makeproject.api.MakeProjectLookupProvider; +import org.openide.util.lookup.ServiceProvider; + +/** + * Adds stuff to MakeProject Lookup. + * @author antonio + */ +@ServiceProvider(service = MakeProjectLookupProvider.class) +public class LSPMakeProjectLookupProvider implements MakeProjectLookupProvider { + + @Override + public void addLookup(MakeProject owner, ArrayList<Object> ic) { + ClangCDBSupport support = new ClangCDBSupport(owner); + ic.add(support); + } + +} diff --git a/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/resources/Bundle.properties b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/resources/Bundle.properties new file mode 100644 index 0000000..caf1327 --- /dev/null +++ b/cnd/cnd.lsp/src/org/netbeans/modules/cnd/lsp/resources/Bundle.properties @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +OpenIDE-Module-Display-Category=C/C++ +# Should not be translated +OpenIDE-Module-Long-Description=\ + C/C++ Language Server Protocol support +OpenIDE-Module-Name=C/C++ LSP +OpenIDE-Module-Short-Description=Language Server Protocol Support for C/C++ diff --git a/cnd/cnd.makeproject/nbproject/project.xml b/cnd/cnd.makeproject/nbproject/project.xml index 8bb88b4..17d2750 100644 --- a/cnd/cnd.makeproject/nbproject/project.xml +++ b/cnd/cnd.makeproject/nbproject/project.xml @@ -345,6 +345,7 @@ <friend>org.netbeans.modules.cnd.gizmo</friend> <friend>org.netbeans.modules.cnd.highlight</friend> <friend>org.netbeans.modules.cnd.jconvertwizard</friend> + <friend>org.netbeans.modules.cnd.lsp</friend> <friend>org.netbeans.modules.cnd.make2netbeans</friend> <friend>org.netbeans.modules.cnd.makeproject.source.bridge</friend> <friend>org.netbeans.modules.cnd.makeproject.ui</friend> diff --git a/cnd/cnd.toolchain/nbproject/project.xml b/cnd/cnd.toolchain/nbproject/project.xml index f3088b5..aece047 100644 --- a/cnd/cnd.toolchain/nbproject/project.xml +++ b/cnd/cnd.toolchain/nbproject/project.xml @@ -246,7 +246,6 @@ <friend>org.netbeans.modules.cnd.analysis.impl</friend> <friend>org.netbeans.modules.cnd.api.remote</friend> <friend>org.netbeans.modules.cnd.audit</friend> - <friend>org.netbeans.modules.cnd.jconvertwizard</friend> <friend>org.netbeans.modules.cnd.classview</friend> <friend>org.netbeans.modules.cnd.cncppunit</friend> <friend>org.netbeans.modules.cnd.completion</friend> @@ -259,6 +258,8 @@ <friend>org.netbeans.modules.cnd.dwarfdiscovery</friend> <friend>org.netbeans.modules.cnd.gizmo</friend> <friend>org.netbeans.modules.cnd.highlight</friend> + <friend>org.netbeans.modules.cnd.jconvertwizard</friend> + <friend>org.netbeans.modules.cnd.lsp</friend> <friend>org.netbeans.modules.cnd.make2netbeans</friend> <friend>org.netbeans.modules.cnd.makeproject</friend> <friend>org.netbeans.modules.cnd.makeproject.ui</friend> diff --git a/ide/dlight.nativeexecution/nbproject/project.xml b/ide/dlight.nativeexecution/nbproject/project.xml index db4cb91..885590a 100644 --- a/ide/dlight.nativeexecution/nbproject/project.xml +++ b/ide/dlight.nativeexecution/nbproject/project.xml @@ -259,6 +259,7 @@ <friend>org.netbeans.modules.cnd.dwarfdiscovery</friend> <friend>org.netbeans.modules.cnd.gizmo</friend> <friend>org.netbeans.modules.cnd.highlight</friend> + <friend>org.netbeans.modules.cnd.lsp</friend> <friend>org.netbeans.modules.cnd.makeproject</friend> <friend>org.netbeans.modules.cnd.makeproject.ui</friend> <friend>org.netbeans.modules.cnd.mixeddev</friend> diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties index 9120a242..97aabf4 100644 --- a/nbbuild/cluster.properties +++ b/nbbuild/cluster.properties @@ -1089,6 +1089,7 @@ nb.cluster.cnd=\ cnd.editor,\ cnd.kit,\ cnd.lexer,\ + cnd.lsp,\ cnd.makeproject,\ cnd.makeproject.source.bridge,\ cnd.makeproject.ui,\ --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists