Merge pull request #6859 from bitrunner/cnd-meson-project

[CND] RFC: add meson build project support
diff --git a/cnd/cnd.kit/nbproject/project.xml b/cnd/cnd.kit/nbproject/project.xml
index cef5482..6de10dd 100644
--- a/cnd/cnd.kit/nbproject/project.xml
+++ b/cnd/cnd.kit/nbproject/project.xml
@@ -120,6 +120,12 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.cnd.meson</code-name-base>
+                    <run-dependency>
+                        <specification-version>1.0</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.cnd.remote</code-name-base>
                     <run-dependency>
                         <specification-version>1.0</specification-version>
diff --git a/cnd/cnd.meson/build.xml b/cnd/cnd.meson/build.xml
new file mode 100644
index 0000000..fddb9b7
--- /dev/null
+++ b/cnd/cnd.meson/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.meson">
+    <description>Builds, tests, and runs the project org.netbeans.modules.cnd.meson</description>
+    <import file="../../nbbuild/templates/projectized.xml"/>
+</project>
diff --git a/cnd/cnd.meson/manifest.mf b/cnd/cnd.meson/manifest.mf
new file mode 100644
index 0000000..cb30e84
--- /dev/null
+++ b/cnd/cnd.meson/manifest.mf
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+AutoUpdate-Show-In-Client: false
+OpenIDE-Module: org.netbeans.modules.cnd.meson
+OpenIDE-Module-Layer: org/netbeans/modules/cnd/meson/resources/layer.xml
+OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/cnd/meson/editor/Bundle.properties
diff --git a/cnd/cnd.meson/nbproject/project.properties b/cnd/cnd.meson/nbproject/project.properties
new file mode 100644
index 0000000..e180d67
--- /dev/null
+++ b/cnd/cnd.meson/nbproject/project.properties
@@ -0,0 +1,19 @@
+# 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.
+javac.source=1.8
+javac.compilerargs=-Xlint -Xlint:-serial
+spec.version.base=1.0
diff --git a/cnd/cnd.meson/nbproject/project.xml b/cnd/cnd.meson/nbproject/project.xml
new file mode 100644
index 0000000..a6c7610
--- /dev/null
+++ b/cnd/cnd.meson/nbproject/project.xml
@@ -0,0 +1,225 @@
+<?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.meson</code-name-base>
+            <module-dependencies>
+                <dependency>
+                    <code-name-base>com.google.gson</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>2.8.9</specification-version>
+                    </run-dependency>
+                </dependency>
+		<dependency>
+                    <code-name-base>org.netbeans.api.templates</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.30</specification-version>
+                    </run-dependency>
+		</dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.core.multiview</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.66</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>
+                        <implementation-version/>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.cnd.ui</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.4.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.editor</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>3</release-version>
+                        <specification-version>1.109</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.editor.lib</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>3</release-version>
+                        <specification-version>4.29</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.extexecution</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>2</release-version>
+                        <specification-version>1.71</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.lexer</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>2</release-version>
+                        <specification-version>1.85</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.93</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.projectuiapi</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.111</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.108</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.awt</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.90</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.filesystems</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>9.35</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.io</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.71</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.loaders</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.92</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.modules</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.70</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.nodes</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.67</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.text</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>6.90</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>9.30</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.56</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util.ui</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>9.31</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.windows</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>6.99</specification-version>
+                    </run-dependency>
+                </dependency>
+            </module-dependencies>
+            <public-packages/>
+        </data>
+    </configuration>
+</project>
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/Bundle.properties b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/Bundle.properties
new file mode 100644
index 0000000..99cd8ef
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/Bundle.properties
@@ -0,0 +1,35 @@
+# 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++
+OpenIDE-Module-Long-Description=\
+    Supports C/C++ projects using the meson build system.  \
+    The project typically is a native development project written in C or C++.  \
+    However, since meson is not limited to these languages, this type of project can be used to build just about anything.
+OpenIDE-Module-Name=C/C++ Meson Build Project
+OpenIDE-Module-Short-Description=Supports C/C++ projects using the meson build system.
+Meson_NewProject=Meson
+Templates_meson=Meson Build Files
+
+comment=Comment
+error=Error
+identifier=Identifier
+keyword=Keyword
+number=Number
+operator=Operator
+string=String
+whitespace=Whitespace
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/MesonBuildKit.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/MesonBuildKit.java
new file mode 100644
index 0000000..9800a4b
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/MesonBuildKit.java
@@ -0,0 +1,29 @@
+/*
+ * 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.meson.editor;
+
+import org.netbeans.modules.cnd.meson.editor.file.MIMETypes;
+
+public class MesonBuildKit extends MesonKit {
+    @Override
+    public String getContentType() {
+        return MIMETypes.MESON_BUILD;
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/MesonKit.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/MesonKit.java
new file mode 100644
index 0000000..5b37460
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/MesonKit.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.meson.editor;
+
+import javax.swing.Action;
+import javax.swing.text.Document;
+import javax.swing.text.TextAction;
+import org.netbeans.editor.BaseDocument;
+import org.netbeans.modules.editor.NbEditorKit;
+
+/**
+ * MesonKit is a meson tool specific extension of NbEditorKit that provides
+ * line commenting editor features.
+ */
+public abstract class MesonKit extends NbEditorKit {
+    private static final String COMMENT_LINE = "#"; //NOI18N
+
+    @Override
+    public Document createDefaultDocument() {
+        Document doc = super.createDefaultDocument();
+        doc.putProperty(BaseDocument.WRITE_LINE_SEPARATOR_PROP, BaseDocument.LS_LF);
+        return doc;
+    }
+
+    @Override
+    protected Action[] createActions() {
+        return
+            TextAction.augmentList(
+                super.createActions(),
+                new Action[] {
+                    new CommentAction(COMMENT_LINE),
+                    new UncommentAction(COMMENT_LINE),
+                    new ToggleCommentAction(COMMENT_LINE)
+                }
+            );
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/MesonOptionsKit.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/MesonOptionsKit.java
new file mode 100644
index 0000000..f99a381
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/MesonOptionsKit.java
@@ -0,0 +1,29 @@
+/*
+ * 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.meson.editor;
+
+import org.netbeans.modules.cnd.meson.editor.file.MIMETypes;
+
+public class MesonOptionsKit extends MesonKit {
+    @Override
+    public String getContentType() {
+        return MIMETypes.MESON_OPTIONS;
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/Bundle.properties b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/Bundle.properties
new file mode 100644
index 0000000..a327b58
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/Bundle.properties
@@ -0,0 +1,18 @@
+# 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.
+
+Source=&Source
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/MIMETypes.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/MIMETypes.java
new file mode 100644
index 0000000..c575d44
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/MIMETypes.java
@@ -0,0 +1,36 @@
+/*
+ * 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.meson.editor.file;
+
+import org.openide.filesystems.MIMEResolver;
+import org.openide.util.NbBundle;
+
+@NbBundle.Messages({
+    "LBL_MESON_BUILD_MIME_RESOLVER=Meson MIME Resolver"
+})
+@MIMEResolver.Registration(
+    displayName = "#LBL_MESON_BUILD_MIME_RESOLVER",
+    position = 139, // anything > 140 causes meson_options.txt to not be recognized
+    resource = "../../resources/mime_resolver.xml"
+)
+public class MIMETypes {
+    public static final String MESON_BUILD = "text/x-meson-build"; //NOI18N
+    public static final String MESON_OPTIONS = "text/x-meson-options"; //NOI18N
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/MesonBuildDataObject.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/MesonBuildDataObject.java
new file mode 100644
index 0000000..d93aef6
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/MesonBuildDataObject.java
@@ -0,0 +1,133 @@
+/*
+ * 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.meson.editor.file;
+
+import java.io.IOException;
+
+import org.netbeans.core.spi.multiview.MultiViewElement;
+import org.netbeans.core.spi.multiview.text.MultiViewEditorElement;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionReferences;
+import org.openide.filesystems.FileObject;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectExistsException;
+import org.openide.loaders.MultiDataObject;
+import org.openide.loaders.MultiFileLoader;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle.Messages;
+import org.openide.windows.TopComponent;
+
+@Messages({
+    "LBL_MESON_BUILD_LOADER=meson.build file loader"
+})
+@DataObject.Registration(
+    mimeType = MIMETypes.MESON_BUILD,
+    iconBase = MesonBuildDataObject.ICON,
+    displayName = "#LBL_MESON_BUILD_LOADER",
+    position = 500
+)
+@ActionReferences({
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_BUILD + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.OpenAction"),
+        position = 100,
+        separatorAfter = 200
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_BUILD + "/Actions",
+        id = @ActionID(category = "Edit", id = "org.openide.actions.CutAction"),
+        position = 300
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_BUILD + "/Actions",
+        id = @ActionID(category = "Edit", id = "org.openide.actions.CopyAction"),
+        position = 400,
+        separatorAfter = 500
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_BUILD + "/Actions",
+        id = @ActionID(category = "Edit", id = "org.openide.actions.DeleteAction"),
+        position = 600
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_BUILD + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.RenameAction"),
+        position = 700,
+        separatorAfter = 800
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_BUILD + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.SaveAsTemplateAction"),
+        position = 900,
+        separatorAfter = 1000
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_BUILD + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.FileSystemAction"),
+        position = 1100,
+        separatorAfter = 1200
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_BUILD + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.ToolsAction"),
+        position = 1300
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_BUILD + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.PropertiesAction"),
+        position = 1400
+    ),
+    @ActionReference(
+        path = "Editors/" + MIMETypes.MESON_BUILD + "/Popup",
+        id = @ActionID(category = "Refactoring", id = "org.netbeans.modules.refactoring.api.ui.WhereUsedAction"),
+        position = 1400
+    ),
+    @ActionReference(
+        path = "Editors/" + MIMETypes.MESON_BUILD + "/Popup",
+        id = @ActionID(category = "Refactoring", id = "org.netbeans.modules.refactoring.api.ui.RenameAction"),
+        position = 1500,
+        separatorAfter = 1550
+    )
+})
+public class MesonBuildDataObject extends MultiDataObject {
+    public static final String ICON = "org/netbeans/modules/cnd/meson/resources/file_icon.png";
+    @SuppressWarnings("this-escape")
+    public MesonBuildDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
+        super(pf, loader);
+        registerEditor(MIMETypes.MESON_BUILD, true);
+    }
+
+    @MultiViewElement.Registration(
+        displayName = "#Source",
+        iconBase = MesonBuildDataObject.ICON, //NOI18N
+        persistenceType = TopComponent.PERSISTENCE_ONLY_OPENED,
+        mimeType = MIMETypes.MESON_BUILD,
+        preferredID = "meson.build.file.source", //NOI18N
+        position = 1)
+    public static MultiViewEditorElement createMultiViewEditorElement(Lookup context) {
+        return new MultiViewEditorElement(context);
+    }
+
+    @Override
+    protected int associateLookup() {
+        return 1;
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/MesonOptionsDataObject.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/MesonOptionsDataObject.java
new file mode 100644
index 0000000..3da9c61
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/MesonOptionsDataObject.java
@@ -0,0 +1,133 @@
+/*
+ * 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.meson.editor.file;
+
+import java.io.IOException;
+
+import org.netbeans.core.spi.multiview.MultiViewElement;
+import org.netbeans.core.spi.multiview.text.MultiViewEditorElement;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionReferences;
+import org.openide.filesystems.FileObject;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectExistsException;
+import org.openide.loaders.MultiDataObject;
+import org.openide.loaders.MultiFileLoader;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle.Messages;
+import org.openide.windows.TopComponent;
+
+@Messages({
+    "LBL_MESON_OPTIONS_LOADER=meson.options file loader"
+})
+@DataObject.Registration(
+    mimeType = MIMETypes.MESON_OPTIONS,
+    iconBase = MesonOptionsDataObject.ICON,
+    displayName = "#LBL_MESON_OPTIONS_LOADER",
+    position = 501
+)
+@ActionReferences({
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_OPTIONS + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.OpenAction"),
+        position = 100,
+        separatorAfter = 200
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_OPTIONS + "/Actions",
+        id = @ActionID(category = "Edit", id = "org.openide.actions.CutAction"),
+        position = 300
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_OPTIONS + "/Actions",
+        id = @ActionID(category = "Edit", id = "org.openide.actions.CopyAction"),
+        position = 400,
+        separatorAfter = 500
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_OPTIONS + "/Actions",
+        id = @ActionID(category = "Edit", id = "org.openide.actions.DeleteAction"),
+        position = 600
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_OPTIONS + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.RenameAction"),
+        position = 700,
+        separatorAfter = 800
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_OPTIONS + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.SaveAsTemplateAction"),
+        position = 900,
+        separatorAfter = 1000
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_OPTIONS + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.FileSystemAction"),
+        position = 1100,
+        separatorAfter = 1200
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_OPTIONS + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.ToolsAction"),
+        position = 1300
+    ),
+    @ActionReference(
+        path = "Loaders/" + MIMETypes.MESON_OPTIONS + "/Actions",
+        id = @ActionID(category = "System", id = "org.openide.actions.PropertiesAction"),
+        position = 1400
+    ),
+    @ActionReference(
+        path = "Editors/" + MIMETypes.MESON_OPTIONS + "/Popup",
+        id = @ActionID(category = "Refactoring", id = "org.netbeans.modules.refactoring.api.ui.WhereUsedAction"),
+        position = 1400
+    ),
+    @ActionReference(
+        path = "Editors/" + MIMETypes.MESON_OPTIONS + "/Popup",
+        id = @ActionID(category = "Refactoring", id = "org.netbeans.modules.refactoring.api.ui.RenameAction"),
+        position = 1500,
+        separatorAfter = 1550
+    )
+})
+public class MesonOptionsDataObject extends MultiDataObject {
+    public static final String ICON = "org/netbeans/modules/cnd/meson/resources/file_icon.png";
+    @SuppressWarnings("this-escape")
+    public MesonOptionsDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
+        super(pf, loader);
+        registerEditor(MIMETypes.MESON_OPTIONS, true);
+    }
+
+    @MultiViewElement.Registration(
+        displayName = "#Source",
+        iconBase = MesonOptionsDataObject.ICON, //NOI18N
+        persistenceType = TopComponent.PERSISTENCE_ONLY_OPENED,
+        mimeType = MIMETypes.MESON_OPTIONS,
+        preferredID = "meson.options.file.source", //NOI18N
+        position = 1)
+    public static MultiViewEditorElement createMultiViewEditorElement(Lookup context) {
+        return new MultiViewEditorElement(context);
+    }
+
+    @Override
+    protected int associateLookup() {
+        return 1;
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/package-info.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/package-info.java
new file mode 100644
index 0000000..c15cf62
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/editor/file/package-info.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+@TemplateRegistrations({
+    @TemplateRegistration(folder = "meson",
+                          targetName = "meson",
+                          content = "../../resources/meson.build.template",
+                          scriptEngine = "freemarker",
+                          category = "meson-types",
+                          displayName = "meson.build",
+                          iconBase = MesonBuildDataObject.ICON),
+    @TemplateRegistration(folder = "meson",
+                          targetName = "meson",
+                          content = "../../resources/meson.options.template",
+                          scriptEngine="freemarker",
+                          category = "meson-types",
+                          displayName = "meson.options",
+                          iconBase = MesonOptionsDataObject.ICON),
+})
+package org.netbeans.modules.cnd.meson.editor.file;
+
+import org.netbeans.api.templates.TemplateRegistration;
+import org.netbeans.api.templates.TemplateRegistrations;
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonBuildLanguageHierarchy.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonBuildLanguageHierarchy.java
new file mode 100644
index 0000000..e2b536c
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonBuildLanguageHierarchy.java
@@ -0,0 +1,51 @@
+/*
+ * 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.meson.lexer;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import org.netbeans.spi.lexer.LanguageHierarchy;
+import org.netbeans.spi.lexer.Lexer;
+import org.netbeans.spi.lexer.LexerRestartInfo;
+
+/**
+ *
+ */
+public class MesonBuildLanguageHierarchy extends LanguageHierarchy<MesonBuildTokenId> {
+    private final String mime;
+
+    public MesonBuildLanguageHierarchy(String mime) {
+        this.mime = mime;
+    }
+
+    @Override
+    protected synchronized Collection<MesonBuildTokenId> createTokenIds() {
+        return EnumSet.allOf(MesonBuildTokenId.class);
+    }
+
+    @Override
+    protected Lexer<MesonBuildTokenId> createLexer(LexerRestartInfo<MesonBuildTokenId> info) {
+        return new MesonBuildLexer(info);
+    }
+
+    @Override
+    protected String mimeType() {
+        return mime;
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonBuildLexer.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonBuildLexer.java
new file mode 100644
index 0000000..136a67c
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonBuildLexer.java
@@ -0,0 +1,287 @@
+/*
+ * 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.meson.lexer;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.netbeans.api.lexer.Token;
+import org.netbeans.spi.lexer.Lexer;
+import org.netbeans.spi.lexer.LexerInput;
+import org.netbeans.spi.lexer.LexerRestartInfo;
+
+/**
+ *
+ */
+public class MesonBuildLexer implements Lexer<MesonBuildTokenId> {
+    private static final Set<String> functions = new HashSet<>();
+    private static final Set<String> objects = new HashSet<>();
+    private static final Set<String> keywords = new HashSet<>();
+    private static final Set<String> literals = new HashSet<>();
+
+    static {
+        functions.add("add_global_arguments"); // NOI18N
+        functions.add("add_global_link_arguments"); // NOI18N
+        functions.add("add_languages"); // NOI18N
+        functions.add("add_project_arguments"); // NOI18N
+        functions.add("add_project_dependencies"); // NOI18N
+        functions.add("add_project_link_arguments"); // NOI18N
+        functions.add("add_test_setup"); // NOI18N
+        functions.add("alias_target"); // NOI18N
+        functions.add("assert"); // NOI18N
+        functions.add("benchmark"); // NOI18N
+        functions.add("both_libraries"); // NOI18N
+        functions.add("build_target"); // NOI18N
+        functions.add("configuration_data"); // NOI18N
+        functions.add("configure_file"); // NOI18N
+        functions.add("custom_target"); // NOI18N
+        functions.add("debug"); // NOI18N
+        functions.add("declare_dependency"); // NOI18N
+        functions.add("dependency"); // NOI18N
+        functions.add("disabler"); // NOI18N
+        functions.add("environment"); // NOI18N
+        functions.add("error"); // NOI18N
+        functions.add("executable"); // NOI18N
+        functions.add("files"); // NOI18N
+        functions.add("find_program"); // NOI18N
+        functions.add("generator"); // NOI18N
+        functions.add("get_option"); // NOI18N
+        functions.add("get_variable"); // NOI18N
+        functions.add("import"); // NOI18N
+        functions.add("include_directories"); // NOI18N
+        functions.add("install_data"); // NOI18N
+        functions.add("install_emptydir"); // NOI18N
+        functions.add("install_headers"); // NOI18N
+        functions.add("install_man"); // NOI18N
+        functions.add("install_subdir"); // NOI18N
+        functions.add("install_symlink"); // NOI18N
+        functions.add("is_disabler"); // NOI18N
+        functions.add("is_variable"); // NOI18N
+        functions.add("jar"); // NOI18N
+        functions.add("join_paths"); // NOI18N
+        functions.add("library"); // NOI18N
+        functions.add("message"); // NOI18N
+        functions.add("project"); // NOI18N
+        functions.add("range"); // NOI18N
+        functions.add("run_command"); // NOI18N
+        functions.add("run_target"); // NOI18N
+        functions.add("set_variable"); // NOI18N
+        functions.add("shared_library"); // NOI18N
+        functions.add("shared_module"); // NOI18N
+        functions.add("static_library"); // NOI18N
+        functions.add("structured_sources"); // NOI18N
+        functions.add("subdir"); // NOI18N
+        functions.add("subdir_done"); // NOI18N
+        functions.add("subproject"); // NOI18N
+        functions.add("summary"); // NOI18N
+        functions.add("test"); // NOI18N
+        functions.add("unset_variable"); // NOI18N
+        functions.add("vcs_tag"); // NOI18N
+        functions.add("warning"); // NOI18N
+
+        objects.add("build_machine"); // NOI18N
+        objects.add("host_machine"); // NOI18N
+        objects.add("meson"); // NOI18N
+        objects.add("target_machine"); // NOI18N
+
+        keywords.add("and"); // NOI18N
+        keywords.add("break"); // NOI18N
+        keywords.add("continue"); // NOI18N
+        keywords.add("elif"); // NOI18N
+        keywords.add("else"); // NOI18N
+        keywords.add("endforeach"); // NOI18N
+        keywords.add("endif"); // NOI18N
+        keywords.add("foreach"); // NOI18N
+        keywords.add("if"); // NOI18N
+        keywords.add("not"); // NOI18N
+        keywords.add("or"); // NOI18N
+
+        literals.add("false"); // NOI18N
+        literals.add("true"); // NOI18N
+    }
+
+    private final LexerRestartInfo<MesonBuildTokenId> info;
+
+    MesonBuildLexer(LexerRestartInfo<MesonBuildTokenId> info) {
+        this.info = info;
+    }
+
+    @Override
+    public Token<MesonBuildTokenId> nextToken () {
+        LexerInput input = info.input ();
+        int i = input.read ();
+        switch (i) {
+            case LexerInput.EOF:
+                return null;
+            case '+':
+            case '<':
+            case '>':
+            case '!':
+            case '=':
+            case ',':
+            case '(':
+            case ')':
+            case '{':
+            case '}':
+            case '[':
+            case ']':
+            case '-':
+            case '*':
+            case '/':
+            case '\\':
+            case ':':
+            case '?':
+            case '.':
+            case '%':
+                return info.tokenFactory().createToken(MesonBuildTokenId.OPERATOR);
+            case ' ':
+            case '\n':
+            case '\r':
+            case '\t':
+                do {
+                    i = input.read();
+                } while ((i == ' ') || (i == '\n') || (i == '\r') || (i == '\t'));
+                if (i != LexerInput.EOF) {
+                    input.backup(1);
+                }
+                return info.tokenFactory().createToken(MesonBuildTokenId.WHITESPACE);
+            case '#':
+                do {
+                    i = input.read();
+                } while ((i != '\n') && (i != '\r') && (i != LexerInput.EOF));
+                return info.tokenFactory().createToken(MesonBuildTokenId.COMMENT);
+            case '\'':
+                // meson supports simple string literals like 'string'
+                // and multiline string literals like '''some really
+                // long string'''
+                if (input.read() == '\'') {
+                    if (input.read() == '\'') {
+                        do {
+                            i = input.read();
+                            if (i == '\'') {
+                                i = input.read();
+                                if (i == '\'') {
+                                    i = input.read();
+                                    if (i == '\'') {
+                                        break;
+                                    } else {
+                                        input.backup(2);
+                                    }
+                                } else {
+                                    input.backup(1);
+                                }
+                            }
+                        } while (i != LexerInput.EOF);
+                        return info.tokenFactory().createToken(MesonBuildTokenId.STRING);
+                    } else {
+                        input.backup(2);
+                    }
+                } else {
+                    input.backup(1);
+                }
+                do {
+                    i = input.read();
+                    if (i == '\\') { // string literals can contain escape sequences
+                        input.read();
+                        i = input.read();
+                    }
+                } while ((i != '\'') && (i != '\n') && (i != '\r') && (i != LexerInput.EOF));
+                return info.tokenFactory().createToken(MesonBuildTokenId.STRING);
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                // meson only supports integer numeric literals
+                // hexadecimal literals are supported starting with 0x
+                // octal literals are supported starting with 0o
+                // binary literals are supported starting with 0b
+                if (i == '0') {
+                    i = input.read();
+                    switch (i) {
+                        case 'x':
+                            do {
+                                i = java.lang.Character.toLowerCase(input.read());
+                            } while (   ((i >= '0') && (i <= '9'))
+                                     || ((i >= 'a') && (i <= 'f')));
+                            input.backup(1);
+                            return info.tokenFactory().createToken(MesonBuildTokenId.NUMBER);
+                        case 'o':
+                            do {
+                                i = input.read();
+                            } while ((i >= '0') && (i <= '7'));
+                            input.backup(1);
+                            return info.tokenFactory().createToken(MesonBuildTokenId.NUMBER);
+                        case 'b':
+                            do {
+                                i = input.read();
+                            } while ((i == '0') || (i == '1'));
+                            input.backup(1);
+                            return info.tokenFactory().createToken(MesonBuildTokenId.NUMBER);
+                    }
+                }
+                do {
+                    i = input.read();
+                } while ((i >= '0') && (i <= '9'));
+                input.backup(1);
+                return info.tokenFactory().createToken(MesonBuildTokenId.NUMBER);
+            default:
+                if (   (i >= 'a' && i <= 'z')
+                    || (i >= 'A' && i <= 'Z')
+                    || (i == '_')) {
+                    do {
+                        i = input.read();
+                    } while (   ((i >= 'a') && (i <= 'z'))
+                             || ((i >= 'A') && (i <= 'Z'))
+                             || ((i >= '0') && (i <= '9'))
+                             || (i == '_'));
+                    input.backup(1);
+                    final String token = input.readText().toString();
+                    if (keywords.contains(token)) {
+                        return info.tokenFactory().createToken(MesonBuildTokenId.KEYWORD);
+                    }
+                    else if (functions.contains(token)) {
+                        return info.tokenFactory().createToken(MesonBuildTokenId.FUNCTION);
+                    }
+                    else if (objects.contains(token)) {
+                        return info.tokenFactory().createToken(MesonBuildTokenId.OBJECT);
+                    }
+                    else if (literals.contains(token)) {
+                        return info.tokenFactory().createToken(MesonBuildTokenId.LITERAL);
+                    }
+                    return info.tokenFactory().createToken(MesonBuildTokenId.IDENTIFIER);
+                }
+                return info.tokenFactory().createToken(MesonBuildTokenId.ERROR);
+        }
+    }
+
+    @Override
+    public Object state() {
+        return null;
+    }
+
+    @Override
+    public void release() {
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonBuildTokenId.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonBuildTokenId.java
new file mode 100644
index 0000000..e9dd105
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonBuildTokenId.java
@@ -0,0 +1,101 @@
+/*
+ * 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.meson.lexer;
+
+import org.netbeans.api.lexer.Language;
+import org.netbeans.api.lexer.TokenId;
+import org.netbeans.modules.cnd.meson.editor.file.MIMETypes;
+
+/**
+ * Meson build script tokens.
+ */
+public enum MesonBuildTokenId implements TokenId {
+    /**
+     * Space, tab, newline, etc. that does not have any special meaning.
+     */
+    WHITESPACE("whitespace"), // NOI18N
+
+    /**
+     * Comment: starts with <code>#</code> and ends at line end.
+     */
+    COMMENT("comment"), // NOI18N
+
+    /**
+     * Meson builtin functions.
+     */
+    FUNCTION("keyword"), // NOI18N
+
+    /**
+     * Meson builtin objects.
+     */
+    OBJECT("keyword"), // NOI18N
+
+    /**
+     * Meson keywords.
+     */
+    KEYWORD("keyword"), // NOI18N
+
+    /**
+     * Meson string literals.
+     */
+    STRING("string"), // NOI18N
+
+    /**
+     * Meson literals (boolean true and false).
+     */
+    LITERAL("literal"), // NOI18N
+
+    /**
+     * Meson number literals.
+     */
+    NUMBER("number"), // NOI18N
+
+    /**
+     * Meson operators.
+     */
+    OPERATOR("operator"), // NOI18N
+
+    /**
+     * Meson identifiers (variable names, etc.)
+     */
+    IDENTIFIER("identifier"), // NOI18N
+
+    /**
+     * This token is returned when something goes wrong while evaluating the source file.
+     */
+    ERROR("error"); // NOI18N
+
+    private final String category;
+
+    private MesonBuildTokenId(String category) {
+        this.category = category;
+    }
+
+    @Override
+    public String primaryCategory() {
+        return category;
+    }
+
+    private static final Language<MesonBuildTokenId> LANGUAGE =
+        new MesonBuildLanguageHierarchy(MIMETypes.MESON_BUILD).language();
+
+    public static Language<MesonBuildTokenId> language() {
+        return LANGUAGE;
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonOptionsLanguageHierarchy.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonOptionsLanguageHierarchy.java
new file mode 100644
index 0000000..28feea1
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonOptionsLanguageHierarchy.java
@@ -0,0 +1,52 @@
+/*
+ * 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.meson.lexer;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import org.netbeans.spi.lexer.LanguageHierarchy;
+import org.netbeans.spi.lexer.Lexer;
+import org.netbeans.spi.lexer.LexerRestartInfo;
+
+/**
+ *
+ */
+public class MesonOptionsLanguageHierarchy extends LanguageHierarchy<MesonOptionsTokenId> {
+    private final String mime;
+
+    public MesonOptionsLanguageHierarchy(String mime) {
+        this.mime = mime;
+    }
+
+    @Override
+    protected synchronized Collection<MesonOptionsTokenId> createTokenIds() {
+        return EnumSet.allOf(MesonOptionsTokenId.class);
+    }
+
+    @Override
+    protected Lexer<MesonOptionsTokenId> createLexer(LexerRestartInfo<MesonOptionsTokenId> info) {
+        return new MesonOptionsLexer(info);
+    }
+
+    @Override
+    protected String mimeType() {
+        return mime;
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonOptionsLexer.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonOptionsLexer.java
new file mode 100644
index 0000000..cc777ff
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonOptionsLexer.java
@@ -0,0 +1,140 @@
+/*
+ * 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.meson.lexer;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.netbeans.api.lexer.Token;
+import org.netbeans.spi.lexer.Lexer;
+import org.netbeans.spi.lexer.LexerInput;
+import org.netbeans.spi.lexer.LexerRestartInfo;
+
+public class MesonOptionsLexer implements Lexer<MesonOptionsTokenId> {
+    private static final Set<String> keywords = new HashSet<>();
+    private static final Set<String> literals = new HashSet<>();
+
+    static {
+        keywords.add("choices"); // NOI18N
+        keywords.add("deprecated"); // NOI18N
+        keywords.add("description"); // NOI18N
+        keywords.add("max"); // NOI18N
+        keywords.add("min"); // NOI18N
+        keywords.add("type"); // NOI18N
+        keywords.add("value"); // NOI18N
+        keywords.add("yield"); // NOI18N
+
+        literals.add("false"); // NOI18N
+        literals.add("true"); // NOI18N
+    }
+
+    private final LexerRestartInfo<MesonOptionsTokenId> info;
+
+    MesonOptionsLexer(LexerRestartInfo<MesonOptionsTokenId> info) {
+        this.info = info;
+    }
+
+    @Override
+    public Token<MesonOptionsTokenId> nextToken () {
+        LexerInput input = info.input ();
+        int i = input.read ();
+        switch (i) {
+            case LexerInput.EOF:
+                return null;
+            case '+':
+            case ',':
+            case '(':
+            case ')':
+            case '{':
+            case '}':
+            case '[':
+            case ']':
+            case ':':
+                return info.tokenFactory().createToken(MesonOptionsTokenId.OPERATOR);
+            case ' ':
+            case '\n':
+            case '\r':
+            case '\t':
+                do {
+                    i = input.read();
+                } while ((i == ' ') || (i == '\n') || (i == '\r') || (i == '\t'));
+                if (i != LexerInput.EOF) {
+                    input.backup(1);
+                }
+                return info.tokenFactory().createToken(MesonOptionsTokenId.WHITESPACE);
+            case '#':
+                do {
+                    i = input.read();
+                } while ((i != '\n') && (i != '\r') && (i != LexerInput.EOF));
+                return info.tokenFactory().createToken(MesonOptionsTokenId.COMMENT);
+            case '\'':
+                // meson options files only support simple string literals like 'string'
+                do {
+                    i = input.read();
+                } while ((i != '\'') && (i != '\n') && (i != '\r') && (i != LexerInput.EOF));
+                return info.tokenFactory().createToken(MesonOptionsTokenId.STRING);
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                // meson options files only support integer numeric literals
+                do {
+                    i = input.read();
+                } while ((i >= '0') && (i <= '9'));
+                input.backup(1);
+                return info.tokenFactory().createToken(MesonOptionsTokenId.NUMBER);
+            default:
+                if (   (i >= 'a' && i <= 'z')
+                    || (i >= 'A' && i <= 'Z')) {
+                    do {
+                        i = input.read();
+                    } while (   ((i >= 'a') && (i <= 'z'))
+                             || ((i >= 'A') && (i <= 'Z')));
+                    input.backup(1);
+                    final String token = input.readText().toString();
+                    if ("option".equals(token)) {
+                        return info.tokenFactory().createToken(MesonOptionsTokenId.OPTION);
+                    }
+                    else if (keywords.contains(token)) {
+                        return info.tokenFactory().createToken(MesonOptionsTokenId.ARGUMENT_KEYWORD);
+                    }
+                    else if (literals.contains(token)) {
+                        return info.tokenFactory().createToken(MesonOptionsTokenId.LITERAL);
+                    }
+                }
+                return info.tokenFactory().createToken(MesonOptionsTokenId.ERROR);
+        }
+    }
+
+    @Override
+    public Object state() {
+        return null;
+    }
+
+    @Override
+    public void release() {
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonOptionsTokenId.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonOptionsTokenId.java
new file mode 100644
index 0000000..320cfc6
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/lexer/MesonOptionsTokenId.java
@@ -0,0 +1,91 @@
+/*
+ * 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.meson.lexer;
+
+import org.netbeans.api.lexer.Language;
+import org.netbeans.api.lexer.TokenId;
+import org.netbeans.modules.cnd.meson.editor.file.MIMETypes;
+
+/**
+ * Meson options file tokens.
+ */
+public enum MesonOptionsTokenId implements TokenId {
+    /**
+     * Space, tab, newline, etc. that does not have any special meaning.
+     */
+    WHITESPACE("whitespace"), // NOI18N
+
+    /**
+     * Comment: starts with <code>#</code> and ends at line end.
+     */
+    COMMENT("comment"), // NOI18N
+
+    /**
+     * Meson option keyword.
+     */
+    OPTION("keyword"), // NOI18N
+
+    /**
+     * Meson option literals (boolean true and false).
+     */
+    LITERAL("literal"), // NOI18N
+
+    /**
+     * Meson option string literals.
+     */
+    STRING("string"), // NOI18N
+
+    /**
+     * Meson option number literals.
+     */
+    NUMBER("number"), // NOI18N
+
+    /**
+     * Meson option operators.
+     */
+    OPERATOR("operator"), // NOI18N
+
+    /**
+     * Meson option argument keywords.
+     */
+    ARGUMENT_KEYWORD("keyword"), // NOI18N
+
+    /**
+     * This token is returned when something goes wrong while evaluating the source file.
+     */
+    ERROR("error"); // NOI18N
+
+    private final String category;
+
+    private MesonOptionsTokenId(String category) {
+        this.category = category;
+    }
+
+    @Override
+    public String primaryCategory() {
+        return category;
+    }
+
+    private static final Language<MesonOptionsTokenId> LANGUAGE =
+        new MesonOptionsLanguageHierarchy(MIMETypes.MESON_OPTIONS).language();
+
+    public static Language<MesonOptionsTokenId> language() {
+        return LANGUAGE;
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ActionProviderImpl.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ActionProviderImpl.java
new file mode 100644
index 0000000..4695cfb
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ActionProviderImpl.java
@@ -0,0 +1,250 @@
+/*
+ * 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.meson.project;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.Action;
+import org.netbeans.api.extexecution.ExecutionDescriptor;
+import org.netbeans.api.extexecution.ExecutionService;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.spi.project.ActionProvider;
+import org.netbeans.spi.project.ui.support.ProjectSensitiveActions;
+import org.openide.LifecycleManager;
+import org.openide.filesystems.FileUtil;
+import org.openide.modules.InstalledFileLocator;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+
+public class ActionProviderImpl implements ActionProvider {
+    public static final String COMMAND_SETUP = "setup"; // NOI18N
+    public static final String COMMAND_DEBUG_TEST = "debug.test"; // NOI18N
+    public static final String COMMAND_DEBUG_STEP_INTO_TEST = "debug.stepinto.test"; // NOI18N
+
+    private static final String[] SUPPORTED_ACTIONS = {
+        COMMAND_SETUP,
+        COMMAND_BUILD,
+        COMMAND_CLEAN,
+        COMMAND_REBUILD,
+        COMMAND_RUN,
+        COMMAND_DEBUG,
+        COMMAND_DEBUG_STEP_INTO,
+        COMMAND_DEBUG_SINGLE,
+        COMMAND_TEST,
+        COMMAND_DEBUG_TEST,
+        COMMAND_DEBUG_STEP_INTO_TEST,
+    };
+
+    private final MesonProject project;
+
+    public ActionProviderImpl(MesonProject project) {
+        this.project = project;
+    }
+
+    @Override
+    @SuppressWarnings("ReturnOfCollectionOrArrayField")
+    public String[] getSupportedActions() {
+        return SUPPORTED_ACTIONS;
+    }
+
+    @Override
+    public void invokeAction(String action, Lookup context) throws IllegalArgumentException {
+        File module = InstalledFileLocator.getDefault().locate("modules/org-netbeans-modules-cnd-meson.jar", "org.netbeans.modules.cnd.meson", false);
+
+        ExecutionDescriptor executionDescriptor =
+            new ExecutionDescriptor()
+                .showProgress(true)
+                .showSuspended(true)
+                .frontWindowOnError(true)
+                .controllable(true)
+                .errConvertorFactory(new LineConvertorFactoryImpl())
+                .outConvertorFactory(new LineConvertorFactoryImpl())
+                .postExecution(
+                    () -> {
+                        FileUtil.refreshFor(Paths.get(project.getProjectDirectory().getPath(), project.getActiveConfiguration().getBuildDirectory(), MesonProject.INFO_DIRECTORY).toFile());
+                    });
+
+        ExecutionService.newService(
+            () -> {
+                LifecycleManager.getDefault().saveAll();
+
+                List<List<String>> commandsToExecute = new ArrayList<>();
+                Configuration cfg = project.getActiveConfiguration();
+                File runDir = FileUtil.toFile(project.getProjectDirectory());
+                File buildDir = Paths.get(project.getProjectDirectory().getPath(), cfg.getBuildDirectory()).toFile();
+
+                if (!buildDirectoryIsValid(buildDir) && !action.equals(COMMAND_SETUP)) {
+                    commandsToExecute.add(getMesonCommandLineFor(COMMAND_SETUP));
+                }
+
+                switch (action)
+                {
+                    case COMMAND_REBUILD:
+                        if (buildDir.exists()) {
+                            commandsToExecute.add(getMesonCommandLineFor(COMMAND_CLEAN));
+                        }
+                        commandsToExecute.add(getMesonCommandLineFor(COMMAND_BUILD));
+                        break;
+                    case COMMAND_RUN:
+                        if (!buildDir.exists()) {
+                            commandsToExecute.add(getMesonCommandLineFor(COMMAND_BUILD));
+                        }
+                        commandsToExecute.add(Arrays.asList(cfg.getRunExecutable(), cfg.getRunArguments()));
+                        if ((cfg.getRunDirectory() != null) && !cfg.getRunDirectory().isEmpty()) {
+                            runDir = Paths.get(project.getProjectDirectory().getPath(), cfg.getRunDirectory()).toFile();
+                        }
+                        break;
+                    case COMMAND_DEBUG:
+                        // TODO
+                        break;
+                    default:
+                        List<String> command = getMesonCommandLineFor(action);
+                        if (command != null) {
+                            commandsToExecute.add(command);
+                        }   break;
+                }
+                String arg = Utils.encode(commandsToExecute);
+                return new ProcessBuilder("java", "-classpath", module.getAbsolutePath(), Runner.class.getName(), arg).directory(runDir).start();
+            },
+            executionDescriptor,
+            ProjectUtils.getInformation(project).getDisplayName() + " - " + action
+        ).run();
+    }
+
+    @Override
+    public boolean isActionEnabled(String command, Lookup context) throws IllegalArgumentException {
+        if (null != command) switch (command)
+        {
+            case COMMAND_DEBUG:
+                return false; //isActionEnabled(COMMAND_RUN, context);
+            case COMMAND_REBUILD:
+                return isActionEnabled(COMMAND_CLEAN, context) && isActionEnabled(COMMAND_BUILD, context);
+            case COMMAND_RUN:
+                Configuration cfg = project.getActiveConfiguration();
+                return !Utils.isEmpty(cfg.getRunExecutable()) && Paths.get(project.getProjectDirectory().getPath(), cfg.getRunExecutable()).toFile().isFile();
+            default:
+                break;
+        }
+        return true;
+    }
+
+    private List<String> getMesonCommandLineFor(String action) {
+        List<String> command = null;
+        Configuration config = project.getActiveConfiguration();
+
+        switch (action) {
+            case COMMAND_BUILD:
+                command = new ArrayList<>();
+                command.add("meson"); //NOI18N
+                command.add("compile"); //NOI18N
+                command.add("-C"); //NOI18N
+                command.add(config.getBuildDirectory());
+                command.add(config.getAdditionalArgumentsFor(action));
+                break;
+
+            case COMMAND_CLEAN:
+                command = new ArrayList<>();
+                command.add("meson"); //NOI18N
+                command.add("compile"); //NOI18N
+                command.add("-C"); //NOI18N
+                command.add(config.getBuildDirectory());
+                command.add("--clean"); //NOI18N
+                break;
+
+            case COMMAND_SETUP:
+                command = new ArrayList<>();
+                command.add("meson"); //NOI18N
+                command.add("setup"); //NOI18N
+                command.add("--wipe"); //NOI18N
+
+                if (!Utils.isEmpty(config.getBuildType())) {
+                    command.add("--buildtype"); //NOI18N
+                    command.add(config.getBuildType());
+                }
+
+                if (!Utils.isEmpty(config.getWrapMode())) {
+                    command.add("--wrap-mode"); //NOI18N
+                    command.add(config.getWrapMode());
+                }
+
+                command.add(config.getAdditionalArgumentsFor(action));
+                command.add(config.getBuildDirectory());
+                break;
+
+            case COMMAND_TEST:
+                command = new ArrayList<>();
+                command.add("meson"); //NOI18N
+                command.add("test"); //NOI18N
+                command.add("-C"); //NOI18N
+                command.add(config.getBuildDirectory());
+                command.add(config.getAdditionalArgumentsFor(action));
+                break;
+        }
+
+        return command;
+    }
+
+    public static final class Runner {
+        /**
+         * @param args the command line arguments
+         */
+        public static void main(String[] args) throws Exception {
+            for (List<String> command : Utils.decode(args[0])) {
+                int result = new ProcessBuilder(command).inheritIO().start().waitFor();
+                if (result != 0) {
+                    System.exit(result);
+                }
+            }
+        }
+    }
+
+    public static Action createSetupAction() {
+        return
+            ProjectSensitiveActions.projectCommandAction(
+                COMMAND_SETUP,
+                NbBundle.getMessage(ActionProviderImpl.class, "LBL_MesonSetupCommand"),
+                MesonProject.getIcon());
+    }
+
+    private static final class MesonInfo {
+        boolean error;
+    };
+
+    private static boolean buildDirectoryIsValid(File buildDirectory) {
+        if (buildDirectory.exists()) {
+            try {
+                MesonInfo info = new Gson().fromJson(new FileReader(Paths.get(buildDirectory.toString(), MesonProject.MESON_INFO_JSON).toFile()), new TypeToken<MesonInfo>(){}.getType());
+                return (info != null) && !info.error;
+            }
+            catch (FileNotFoundException ignored) {
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/Bundle.properties b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/Bundle.properties
new file mode 100644
index 0000000..cf4d59f
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/Bundle.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.
+OpenIDE-Module-Display-Category=C/C++
+OpenIDE-Module-Name=C/C++ Meson Projects
+OpenIDE-Module-Short-Description=Support for Meson Build Based Projects
+LBL_MesonSetupCommand=Setup
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/Configuration.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/Configuration.java
new file mode 100644
index 0000000..602e741
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/Configuration.java
@@ -0,0 +1,160 @@
+/*
+ * 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.meson.project;
+
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.netbeans.spi.project.ProjectConfiguration;
+
+public class Configuration implements ProjectConfiguration
+{
+    private Map<String, String> additionalArguments;
+    private String buildDirectory;
+    private String buildType;
+    private String backend;
+    private String name;
+    private String runArguments;
+    private String runDirectory;
+    private String runExecutable;
+    private String wrapMode;
+
+    public Configuration(
+        String name, String buildDirectory, String buildType, String backend,
+        String wrapMode, String runDirectory, String runExecutable, String runArguments,
+        Map<String, String> additionalArguments) {
+        this.name = name;
+        this.buildDirectory = buildDirectory;
+        this.buildType = buildType;
+        this.backend = backend;
+        this.runArguments = runArguments;
+        this.runDirectory = runDirectory;
+        this.runExecutable = runExecutable;
+        this.wrapMode = wrapMode;
+        this.additionalArguments = additionalArguments;
+    }
+
+    @SuppressWarnings("AccessingNonPublicFieldOfAnotherObject")
+    public Configuration(Configuration cfg) {
+        name = cfg.name;
+        buildDirectory = cfg.buildDirectory;
+        buildType = cfg.buildType;
+        backend = cfg.backend;
+        runArguments = cfg.runArguments;
+        runDirectory = cfg.runDirectory;
+        runExecutable = cfg.runExecutable;
+        wrapMode = cfg.wrapMode;
+        additionalArguments = new HashMap<>();
+        additionalArguments.putAll(cfg.additionalArguments);
+    }
+
+    public Configuration() {} // This is only here to make GSON happy
+
+    @Override
+    public String getDisplayName() {
+        return name;
+    }
+
+    public String getAdditionalArgumentsFor(String action) {
+        return additionalArguments.containsKey(action) ? additionalArguments.get(action) : "";
+    }
+
+    public String getBackend() {
+        return backend;
+    }
+
+    public String getBuildDirectory() {
+        return buildDirectory;
+    }
+
+    public String getBuildType() {
+        return buildType;
+    }
+
+    public String getCompileCommandsJsonPath() {
+        return Paths.get(getBuildDirectory(), "compile_commands.json").toString();
+    }
+
+    public String getRunArguments() {
+        return runArguments;
+    }
+
+    public String getRunDirectory() {
+        return runDirectory;
+    }
+
+    public String getRunExecutable() {
+        return runExecutable;
+    }
+
+    public String getWrapMode() {
+        return wrapMode;
+    }
+
+    public void setAdditionalArgumentsFor(String action, String arguments) {
+        additionalArguments.put(action, arguments);
+    }
+
+    public void setBackend(String backend) {
+        this.backend = backend;
+    }
+
+    public void setBuildDirectory(String directory) {
+        buildDirectory = directory;
+    }
+
+    public void setBuildType(String type) {
+        buildType = type;
+    }
+
+    public void setDisplayName(String name) {
+        this.name = name;
+    }
+
+    public void setRunArguments(String arguments) {
+        runArguments = arguments;
+    }
+
+    public void setRunDirectory(String directory) {
+        runDirectory = directory;
+    }
+
+    public void setRunExecutable(String executable) {
+        runExecutable = executable;
+    }
+
+    public void setWrapMode(String mode) {
+        wrapMode = mode;
+    }
+
+    public static Configuration getDefault() {
+        return new Configuration(
+            "default",
+            Paths.get(MesonProject.BUILD_DIRECTORY, "default").toString(),
+            "debug",
+            "ninja",
+            "default",
+            "",
+            "",
+            "",
+            new HashMap<>());
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ConfigurationProvider.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ConfigurationProvider.java
new file mode 100644
index 0000000..c71cda3
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ConfigurationProvider.java
@@ -0,0 +1,300 @@
+/*
+ * 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.meson.project;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.netbeans.spi.project.ProjectConfigurationProvider;
+import org.netbeans.spi.project.ui.CustomizerProvider2;
+import org.openide.filesystems.FileChangeListener;
+import org.openide.filesystems.FileAttributeEvent;
+import org.openide.filesystems.FileEvent;
+import org.openide.filesystems.FileRenameEvent;
+import org.openide.filesystems.FileUtil;
+
+public class ConfigurationProvider implements ProjectConfigurationProvider<Configuration>
+{
+    private static final Logger LOGGER = Logger.getLogger(ConfigurationProvider.class.getName());
+    private static final String CONFIGURATIONS_JSON = "configurations.json"; // NOI18N
+
+    private final MesonProject project;
+    private Configuration activeConfiguration = null;
+    private List<Configuration> configurations = null;
+    private IntroCompilersJsonListener compilersListener = null;
+
+    public ConfigurationProvider(MesonProject project) {
+        this.project = project;
+
+        File configurationsFile = new File(getConfigurationsFilePath());
+
+        if (configurationsFile.exists()) {
+            try {
+                ConfigurationsFileContent content = new Gson().fromJson(new FileReader(configurationsFile), new TypeToken<ConfigurationsFileContent>(){}.getType());
+
+                if ((content != null) && (content.configurations != null)) {
+                    configurations = content.configurations;
+
+                    if (content.activeConfigurationName != null) {
+                        for (Configuration c: configurations) {
+                            if (content.activeConfigurationName.equals(c.getDisplayName())) {
+                                activeConfiguration = c;
+                                break;
+                            }
+                        }
+                    }
+
+                    if (activeConfiguration == null) {
+                        final String defaultName = Configuration.getDefault().getDisplayName();
+
+                        for (Configuration c: configurations) {
+                            if (defaultName.equals(c.getDisplayName())) {
+                                activeConfiguration = c;
+                                break;
+                            }
+                        }
+                    }
+
+                    if ((activeConfiguration == null) && !configurations.isEmpty()) {
+                        activeConfiguration = configurations.get(0);
+                    }
+
+                    if (activeConfiguration == null) {
+                        activeConfiguration = Configuration.getDefault();
+                        configurations.add(activeConfiguration);
+                    }
+                }
+            }
+            catch (FileNotFoundException ex) {
+                LOGGER.log(Level.INFO, null, ex);
+            }
+        }
+
+        if (configurations == null) {
+            configurations = new ArrayList<>();
+            activeConfiguration = Configuration.getDefault();
+            configurations.add(activeConfiguration);
+        }
+
+        File compilersFile = getActiveIntroCompilersJsonPath(project, activeConfiguration).toFile();
+
+        if (compilersFile.exists()) {
+            updateLanguages(project, compilersFile);
+        }
+
+        FileUtil.addFileChangeListener(compilersListener = new IntroCompilersJsonListener(project), compilersFile);
+    }
+
+    public void add(Configuration configuration) {
+        configurations.add(configuration);
+        if (activeConfiguration == null) {
+            setActiveConfiguration(configuration);
+        }
+    }
+
+    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
+    public void setConfigurations(List<Configuration> configurations) {
+        final String activeConfigurationName = getActiveConfiguration().getDisplayName();
+        this.configurations = configurations;
+        activeConfiguration = null;
+        for (Configuration cfg: configurations) {
+            if (activeConfigurationName.equals(cfg.getDisplayName())) {
+                setActiveConfiguration(cfg);
+                break;
+            }
+        }
+        if (activeConfiguration == null && !configurations.isEmpty()) {
+            setActiveConfiguration(configurations.get(0));
+        }
+    }
+
+    public synchronized void save() {
+        try {
+            Files.createDirectories(Paths.get(getConfigurationsFilePath()).getParent());
+
+            try (FileWriter writer = new FileWriter(getConfigurationsFilePath())) {
+                GsonBuilder builder = new GsonBuilder();
+                builder.setPrettyPrinting();
+                writer.write(builder.create().toJson(new ConfigurationsFileContent(activeConfiguration, configurations)));
+            }
+        }
+        catch (IOException ex) {
+            LOGGER.log(Level.WARNING, null, ex);
+        }
+    }
+
+    @Override
+    public Collection<Configuration> getConfigurations() {
+        return Collections.unmodifiableCollection(configurations);
+    }
+
+    @Override
+    public Configuration getActiveConfiguration() {
+        return activeConfiguration;
+    }
+
+    @Override
+    public void setActiveConfiguration(Configuration configuration) {
+        if (activeConfiguration != configuration) {
+            if ((activeConfiguration != null) && (compilersListener != null)) {
+                FileUtil.removeFileChangeListener(compilersListener, getActiveIntroCompilersJsonPath(project).toFile());
+            }
+
+            activeConfiguration = configuration;
+
+            File compilersFile = getActiveIntroCompilersJsonPath(project).toFile();
+
+            if (compilersFile.exists()) {
+                updateLanguages(project, compilersFile);
+            }
+
+            FileUtil.addFileChangeListener(compilersListener = new IntroCompilersJsonListener(project), compilersFile);
+        }
+    }
+
+    @Override
+    public boolean hasCustomizer() {
+        return true;
+    }
+
+    @Override
+    public void customize() {
+        project.getLookup().lookup(CustomizerProvider2.class).showCustomizer();
+    }
+
+    @Override
+    public boolean configurationsAffectAction(String command) {
+        return true;
+    }
+
+    @Override
+    public void addPropertyChangeListener(PropertyChangeListener lst) {
+    }
+
+    @Override
+    public void removePropertyChangeListener(PropertyChangeListener lst) {
+    }
+
+    private String getConfigurationsFilePath() {
+        return Paths.get(project.getProjectDirectory().getPath(), MesonProject.NBPROJECT_DIRECTORY, CONFIGURATIONS_JSON).toString();
+    }
+
+    private static Path getActiveIntroCompilersJsonPath(MesonProject project) {
+        return getActiveIntroCompilersJsonPath(project, project.getActiveConfiguration());
+    }
+
+    private static Path getActiveIntroCompilersJsonPath(MesonProject project, Configuration configuration) {
+        return Paths.get(project.getProjectDirectory().getPath(), configuration.getBuildDirectory(), MesonProject.COMPILERS_JSON);
+    }
+
+    private static final class ConfigurationsFileContent {
+        public String activeConfigurationName;
+        public List<Configuration> configurations;
+
+        public ConfigurationsFileContent() {}
+        public ConfigurationsFileContent(Configuration activeConfiguration, List<Configuration> configurations) {
+            this.activeConfigurationName = activeConfiguration.getDisplayName();
+            this.configurations = configurations;
+        }
+    };
+
+    private static final class IntroCompilersJsonListener implements FileChangeListener {
+        private final MesonProject project;
+
+        public IntroCompilersJsonListener(MesonProject project) {
+            this.project = project;
+        }
+
+        @Override
+        public void fileAttributeChanged(FileAttributeEvent e) {}
+
+        @Override
+        public void fileChanged(FileEvent e) {
+            updateLanguages(project);
+        }
+
+        @Override
+        public void fileDataCreated(FileEvent e) {
+            updateLanguages(project);
+        }
+
+        @Override
+        public void fileDeleted(FileEvent e) {}
+
+        @Override
+        public void fileFolderCreated(FileEvent e) {}
+
+        @Override
+        public void fileRenamed(FileRenameEvent e) {}
+    };
+
+    private static final class Compiler {
+        String id;
+        String[] exelist;
+        String[] linker_exelist;
+        String[] file_suffixes;
+        String default_suffix;
+        String version;
+        String full_version;
+        String linker_id;
+    };
+
+    private static void updateLanguages(MesonProject project) {
+        updateLanguages(project, getActiveIntroCompilersJsonPath(project).toFile());
+    }
+
+    private static void updateLanguages(MesonProject project, File compilersFile) {
+        try {
+            Map<String, Map<String, Compiler>> machines = new Gson().fromJson(new FileReader(compilersFile), new TypeToken<Map<String, Map<String, Compiler>>>(){}.getType());
+
+            if (machines != null) {
+                Map<String, Compiler> host = machines.get("host");
+                if (host != null) {
+                    Set<String> languages = host.keySet();
+                    if (!languages.isEmpty()) {
+                        project.setLanguages(languages);
+                    }
+                }
+            }
+        }
+        catch (FileNotFoundException ex) {
+            LOGGER.log(Level.INFO, null, ex);
+        }
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/LineConvertorFactoryImpl.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/LineConvertorFactoryImpl.java
new file mode 100644
index 0000000..4a0c02e
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/LineConvertorFactoryImpl.java
@@ -0,0 +1,76 @@
+/*
+ * 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.meson.project;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.netbeans.api.extexecution.ExecutionDescriptor.LineConvertorFactory;
+import org.netbeans.api.extexecution.print.ConvertedLine;
+import org.netbeans.api.extexecution.print.LineConvertor;
+import org.openide.cookies.LineCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.text.Line;
+import org.openide.windows.OutputEvent;
+import org.openide.windows.OutputListener;
+
+public class LineConvertorFactoryImpl implements LineConvertorFactory
+{
+    @Override
+    public LineConvertor newLineConvertor()
+    {
+        return new MesonLineConvertor();
+    }
+
+    // This thing works for meson, gcc, and clang output.  It might work for other things, but
+    // it would probably be best to use the toolchain appropriate instance of
+    // org.netbeans.modules.cnd.spi.toolchain.CompilerLineConvertor for compiler output.
+    private static final class MesonLineConvertor implements LineConvertor {
+        private static final Pattern ERROR_LINE = Pattern.compile("(.*):(\\d+):(\\d+):.*");
+        @Override
+        public List<ConvertedLine> convert(String line) {
+            Matcher matcher = ERROR_LINE.matcher(line);
+            if (matcher.matches()) {
+                String fileName = matcher.group(1);
+                int lineNum = Integer.parseInt(matcher.group(2)) - 1;
+                int columnNum = Integer.parseInt(matcher.group(3)) - 1;
+                return Collections.singletonList(ConvertedLine.forText(line, new OutputListener() {
+                    @Override
+                    public void outputLineSelected(OutputEvent ev) {}
+                    @Override
+                    public void outputLineAction(OutputEvent ev) {
+                        FileObject file = FileUtil.toFileObject(new File(fileName));
+                        if (file != null) {
+                            LineCookie lc = file.getLookup().lookup(LineCookie.class);
+                            lc.getLineSet().getCurrent(lineNum).show(Line.ShowOpenType.OPEN, Line.ShowVisibilityType.FOCUS, columnNum);
+                        }
+                    }
+                    @Override
+                    public void outputLineCleared(OutputEvent ev) {}
+                }));
+            }
+            return Collections.singletonList(ConvertedLine.forText(line, null));
+        }
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/LogicalViewProviderImpl.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/LogicalViewProviderImpl.java
new file mode 100644
index 0000000..6169307
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/LogicalViewProviderImpl.java
@@ -0,0 +1,77 @@
+/*
+ * 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.meson.project;
+
+import java.awt.Image;
+
+import javax.swing.Action;
+import org.netbeans.spi.project.ui.LogicalViewProvider;
+import org.netbeans.spi.project.ui.support.CommonProjectActions;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectNotFoundException;
+import org.openide.nodes.FilterNode;
+import org.openide.nodes.Node;
+import org.openide.util.ImageUtilities;
+import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ProxyLookup;
+
+public class LogicalViewProviderImpl implements LogicalViewProvider {
+
+    private final MesonProject project;
+
+    public LogicalViewProviderImpl(MesonProject project) {
+        this.project = project;
+    }
+
+    @Override
+    public Node createLogicalView() {
+        try {
+            return new RootNode(DataObject.find(project.getProjectDirectory()).getNodeDelegate(), project);
+        } catch (DataObjectNotFoundException ex) {
+            return Node.EMPTY;
+        }
+    }
+
+    @Override
+    public Node findPath(Node root, Object target) {
+        return null; //XXX
+    }
+
+    private static class RootNode extends FilterNode {
+
+        public RootNode(Node delegate, MesonProject project) {
+            super(delegate, null, new ProxyLookup(delegate.getLookup(), Lookups.fixed(project)));
+        }
+
+        @Override
+        public Image getIcon(int type) {
+            return ImageUtilities.loadImage(MesonProject.ICON); // NOI18N
+        }
+
+        @Override
+        public Image getOpenedIcon(int type) {
+            return ImageUtilities.loadImage(MesonProject.OPEN_ICON); // NOI18N
+        }
+
+        @Override
+        public Action[] getActions(boolean param) {
+            return CommonProjectActions.forType(MesonProject.PROJECT_KEY); // NOI18N
+        }
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/MesonProject.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/MesonProject.java
new file mode 100644
index 0000000..0f65637
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/MesonProject.java
@@ -0,0 +1,278 @@
+/*
+ * 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.meson.project;
+
+import java.beans.PropertyChangeListener;
+import java.nio.file.Paths;
+import java.util.Set;
+
+import javax.swing.Icon;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectInformation;
+import org.netbeans.modules.cnd.meson.project.ui.CustomizerProviderImpl;
+import org.netbeans.spi.project.ProjectState;
+import org.netbeans.spi.project.ui.PrivilegedTemplates;
+import org.netbeans.spi.project.ui.RecommendedTemplates;
+import org.openide.filesystems.FileObject;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+
+public class MesonProject implements Project {
+    public static final String PROJECT_KEY = "org-netbeans-modules-cnd-meson-project"; // NOI18N
+    public static final String NBPROJECT_DIRECTORY = ".netbeans"; // NOI18N
+    public static final String BUILD_DIRECTORY = ".netbeans/build"; // NOI18N
+    public static final String PROJECT_JSON = "project.json"; // NOI18N
+    public static final String ICON = "org/netbeans/modules/cnd/meson/resources/project_icon.png"; // NOI18N
+    public static final String OPEN_ICON = ICON;
+    public static final String INFO_DIRECTORY = "meson-info";
+    public static final String COMPILERS_JSON = Paths.get(INFO_DIRECTORY, "intro-compilers.json").toString();
+    public static final String MESON_INFO_JSON = Paths.get(INFO_DIRECTORY, "meson-info.json").toString();
+
+    private static final String C_LANGUAGE = "c";
+    private static final String CPP_LANGUAGE = "cpp";
+    private static final String FORTRAN_LANGUAGE = "fortran";
+    private static final String JAVA_LANGUAGE = "java";
+    private static final String RUST_LANGUAGE = "rust";
+
+    private final FileObject projectDirectory;
+    private final ProjectState state;
+    private final Lookup lookup;
+    private final ConfigurationProvider configurationProvider;
+    private Set<String> languages;
+
+    @Override
+    public FileObject getProjectDirectory() {
+        return projectDirectory;
+    }
+
+    @Override
+    public Lookup getLookup() {
+        return lookup;
+    }
+
+    public ConfigurationProvider getConfigurationProvider() {
+        return configurationProvider;
+    }
+
+    public Configuration getActiveConfiguration() {
+        return configurationProvider.getActiveConfiguration();
+    }
+
+    public String getCompileCommandsJsonPathForActiveConfiguration() {
+        return Paths.get(getProjectDirectory().getPath(), configurationProvider.getActiveConfiguration().getCompileCommandsJsonPath()).toString();
+    }
+
+    public void setActiveConfiguration(Configuration configuration) {
+        configurationProvider.setActiveConfiguration(configuration);
+    }
+
+    public void setLanguages(Set<String> languages) {
+        this.languages = languages;
+    }
+
+    public boolean isC() { return hasLanguage(C_LANGUAGE); }
+    public boolean isCPP() { return hasLanguage(CPP_LANGUAGE); }
+    public boolean isFortran() { return hasLanguage(FORTRAN_LANGUAGE); }
+    public boolean isJava() { return hasLanguage(JAVA_LANGUAGE); }
+    public boolean isRust() { return hasLanguage(RUST_LANGUAGE); }
+
+    private boolean hasLanguage(String language) {
+        return languages != null && languages.contains(language);
+    }
+
+    @SuppressWarnings({"LeakingThisInConstructor", "this-escape"})
+    public MesonProject(FileObject projectDirectory, ProjectState state) {
+        this.projectDirectory = projectDirectory;
+        this.state = state;
+        this.configurationProvider = new ConfigurationProvider(this);
+        this.lookup = Lookups.fixed(new LogicalViewProviderImpl(this),
+                                    new ActionProviderImpl(this),
+                                    new CustomizerProviderImpl(this),
+                                    new RecommendedTemplatesImpl(this),
+                                    new PrivilegedTemplatesImpl(this),
+                                    new ProjectInfo(this),
+                                    configurationProvider,
+                                    this);
+    }
+
+    public static Icon getIcon() {
+        return ImageUtilities.image2Icon(ImageUtilities.loadImage(ICON));
+    }
+
+    private static class RecommendedTemplatesImpl implements RecommendedTemplates {
+        private static final String[] MESON_TYPES = new String[] {
+            "meson-types", // NOI18N
+            "simple-files", // NOI18N
+        };
+        private static final String[] C_TYPES = new String[] {
+            "c-types", // NOI18N
+        };
+        private static final String[] CPP_TYPES = new String[] {
+            "cpp-types", // NOI18N
+        };
+        private static final String[] FORTRAN_TYPES = new String[] {
+            "fortran-types", // NOI18N
+        };
+        private static final String[] JAVA_TYPES = new String[] {
+            "java-classes", // NOI18N
+            "java-main-class", // NOI18N
+            "java-forms", // NOI18N
+            "gui-java-application", // NOI18N
+            "java-beans", // NOI18N
+            "oasis-XML-catalogs", // NOI18N
+            "XML", // NOI18N
+            "junit", // NOI18N
+        };
+        private static final String[] RUST_TYPES = new String[] {
+            "rust", // NOI18N
+            "XML", // NOI18N
+        };
+
+        private final MesonProject project;
+
+        public RecommendedTemplatesImpl(MesonProject project) {
+            this.project = project;
+        }
+
+        @Override
+        public String[] getRecommendedTypes() {
+            String[] templates = MESON_TYPES;
+
+            if (project.isC()) {
+                templates = Utils.concatenate(templates, C_TYPES);
+            }
+
+            if (project.isCPP()) {
+                templates = Utils.concatenate(templates, CPP_TYPES);
+            }
+
+            if (project.isFortran()) {
+                templates = Utils.concatenate(templates, FORTRAN_TYPES);
+            }
+
+            if (project.isJava()) {
+                templates = Utils.concatenate(templates, JAVA_TYPES);
+            }
+
+            if (project.isRust()) {
+                templates = Utils.concatenate(templates, RUST_TYPES);
+            }
+
+            return templates;
+        }
+    }
+
+    private static class PrivilegedTemplatesImpl implements PrivilegedTemplates {
+        private static final String[] MESON_TEMPLATES = new String[] {
+            "Templates/meson/meson.build",   // NOI18N
+            "Templates/meson/meson.options", // NOI18N
+        };
+        private static final String[] C_TEMPLATES = new String[] {
+            "Templates/cFiles/main.c", // NOI18N
+            "Templates/cFiles/file.c", // NOI18N
+            "Templates/cFiles/file.h", // NOI18N
+        };
+        private static final String[] CPP_TEMPLATES = new String[] {
+            "Templates/cppFiles/class.cc", // NOI18N
+            "Templates/cppFiles/main.cc",  // NOI18N
+            "Templates/cppFiles/file.cc",  // NOI18N
+            "Templates/cppFiles/file.h",   // NOI18N
+        };
+        private static final String[] FORTRAN_TEMPLATES = new String[] {
+            "Templates/fortranFiles/fortranFreeFormatFile.f90", // NOI18N
+        };
+        private static final String[] JAVA_TEMPLATES = new String[] {
+            "Templates/Classes/Class.java",          // NOI18N
+            "Templates/Classes/Interface.java",      // NOI18N
+            "Templates/Other/properties.properties", // NOI18N
+        };
+        private static final String[] RUST_TEMPLATES = new String[] {
+            "Templates/rust/rust-file.rs", // NOI18N
+        };
+
+        private final MesonProject project;
+
+        public PrivilegedTemplatesImpl(MesonProject project) {
+            this.project = project;
+        }
+
+        @Override
+        public String[] getPrivilegedTemplates() {
+            String[] templates = MESON_TEMPLATES;
+
+            if (project.isC()) {
+                templates = Utils.concatenate(templates, C_TEMPLATES);
+            }
+
+            if (project.isCPP()) {
+                templates = Utils.concatenate(templates, CPP_TEMPLATES);
+            }
+
+            if (project.isFortran()) {
+                templates = Utils.concatenate(templates, FORTRAN_TEMPLATES);
+            }
+
+            if (project.isJava()) {
+                templates = Utils.concatenate(templates, JAVA_TEMPLATES);
+            }
+
+            if (project.isRust()) {
+                templates = Utils.concatenate(templates, RUST_TEMPLATES);
+            }
+
+            return templates;
+        }
+    }
+
+    private static final class ProjectInfo implements ProjectInformation {
+        private final Project project;
+
+        public ProjectInfo(Project project) {
+            this.project = project;
+        }
+
+        @Override
+        public String getName() {
+            return project.getProjectDirectory().getNameExt();
+        }
+
+        @Override
+        public String getDisplayName() {
+            return project.getProjectDirectory().getNameExt();
+        }
+
+        @Override
+        public Icon getIcon() {
+            return MesonProject.getIcon();
+        }
+
+        @Override
+        public Project getProject() {
+            return project;
+        }
+
+        @Override
+        public void addPropertyChangeListener(PropertyChangeListener listener) {}
+
+        @Override
+        public void removePropertyChangeListener(PropertyChangeListener listener) {}
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/MesonProjectFactory.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/MesonProjectFactory.java
new file mode 100644
index 0000000..f5184c9
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/MesonProjectFactory.java
@@ -0,0 +1,88 @@
+/*
+ * 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.meson.project;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.netbeans.api.lexer.InputAttributes;
+import org.netbeans.api.lexer.TokenHierarchy;
+import org.netbeans.api.lexer.TokenSequence;
+import org.netbeans.api.lexer.TokenUtilities;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.modules.cnd.meson.lexer.MesonBuildTokenId;
+import org.netbeans.spi.project.ProjectFactory;
+import org.netbeans.spi.project.ProjectFactory2;
+import org.netbeans.spi.project.ProjectState;
+import org.openide.filesystems.FileObject;
+import org.openide.util.lookup.ServiceProvider;
+
+@ServiceProvider(service=ProjectFactory.class, position = 200)
+public class MesonProjectFactory implements ProjectFactory2 {
+    private static final Logger LOGGER = Logger.getLogger(MesonProjectFactory.class.getName());
+
+    @Override
+    public ProjectManager.Result isProject2(FileObject projectDirectory) {
+        FileObject script = projectDirectory.getFileObject("meson.build"); // NOI18N
+        if ((script != null) && script.isValid()) {
+            // The first statement in a top-level meson.build file must be a
+            // call to project(...).  Leverage that to avoid detecting meson.build
+            // files in subdirectories as meson projects.
+            try {
+                TokenSequence<MesonBuildTokenId> tokens =
+                    TokenHierarchy.create(
+                        script.asText(),
+                        false,
+                        MesonBuildTokenId.language(),
+                        Stream.of(MesonBuildTokenId.WHITESPACE, MesonBuildTokenId.COMMENT).collect(Collectors.toSet()),
+                        new InputAttributes()).tokenSequence(MesonBuildTokenId.language());
+
+                if (tokens.moveNext() && TokenUtilities.equals(tokens.token().text(), "project")) {
+                    return new ProjectManager.Result(null, MesonProjectType.TYPE, MesonProject.getIcon());
+                }
+            }
+            catch (IOException ex) {
+                LOGGER.log(Level.INFO, null, ex);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isProject(FileObject projectDirectory) {
+        return isProject2(projectDirectory) != null;
+    }
+
+    @Override
+    public Project loadProject(FileObject projectDirectory, ProjectState state) throws IOException {
+        if (isProject(projectDirectory)) {
+            return new MesonProject(projectDirectory, state);
+        }
+        return null;
+    }
+
+    @Override
+    public void saveProject(Project project) throws IOException, ClassCastException {
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/MesonProjectType.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/MesonProjectType.java
new file mode 100644
index 0000000..7da73db
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/MesonProjectType.java
@@ -0,0 +1,41 @@
+/*
+ * 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.meson.project;
+
+import org.netbeans.modules.cnd.api.project.NativeProjectType;
+
+public class MesonProjectType implements NativeProjectType {
+    public static final String TYPE = "org.netbeans.modules.cnd.meson.project"; // NOI18N
+
+    @Override
+    public String getType() {
+        return TYPE;
+    }
+
+    @Override
+    public String getPrimaryConfigurationDataElementName(boolean shared) {
+        return "data";
+    }
+
+    @Override
+    public String getPrimaryConfigurationDataElementNamespace(boolean shared) {
+        return "";
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/Utils.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/Utils.java
new file mode 100644
index 0000000..2ca880b
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/Utils.java
@@ -0,0 +1,73 @@
+/*
+ * 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.meson.project;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class Utils {
+    public static String encode(List<List<String>> toSave) {
+        return toSave.stream().map(c -> escape(c.stream().map(p -> escape(p)).collect(Collectors.joining(" ")))).collect(Collectors.joining(" "));
+    }
+
+    public static List<List<String>> decode(String toDecode) {
+        List<List<String>> result = new ArrayList<>();
+        for (String commandDesc : toDecode.split(" ")) {
+            List<String> command = new ArrayList<>();
+            for (String paramDesc : unescape(commandDesc).split(" ")) {
+                command.add(unescape(paramDesc));
+            }
+            result.add(command);
+        }
+        return result;
+    }
+
+    public static boolean isEmpty(String s) {
+        return s == null || s.isEmpty();
+    }
+
+    public static String[] concatenate(String[] a1, String[] a2) {
+        String[] combined = null;
+
+        if (a1 != null) {
+            combined = Arrays.copyOf(a1, a1.length + ((a2 != null) ? a2.length : 0));
+        }
+
+        if (a2 != null) {
+            if (combined != null) {
+                System.arraycopy(a2, 0, combined, a1.length, a2.length);
+            }
+            else {
+                combined = Arrays.copyOf(a2, a2.length);
+            }
+        }
+
+        return combined;
+    }
+
+    private static String escape(String s) {
+        return s.replace("_", "_u_").replace(" ", "_s_");
+    }
+
+    private static String unescape(String s) {
+        return s.replace("_s_", " ").replace("_u_", "_");
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/Bundle.properties b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/Bundle.properties
new file mode 100644
index 0000000..b308c9b
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/Bundle.properties
@@ -0,0 +1,34 @@
+# 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.
+ConfigurationPanel.backendLabel.text=Backend
+ConfigurationPanel.wrapModeLabel.text=Wrap Mode
+ConfigurationPanel.buildTypeLabel.text=Build Type
+ConfigurationPanel.buildDirectoryTextField.text=build
+ConfigurationPanel.buildDirectoryLabel.text=Build Directory
+ConfigurationPanel.buildDirectoryBrowseButton.text=...
+ConfigurationPanel.configurationLabel.text=Configuration
+ConfigurationPanel.newConfigurationButton.text=New
+ConfigurationPanel.renameConfigurationButton.text=Rename
+ConfigurationPanel.deleteConfigurationButton.text=Delete
+ConfigurationPanel.runExecutableLabel.text=Run Executable
+ConfigurationPanel.runExecutableTextField.text=
+ConfigurationPanel.runExecutableBrowseButton.text=...
+ConfigurationPanel.runArgumentsLabel.text=Run Arguments
+ConfigurationPanel.runDirectoryTextField.text=
+ConfigurationPanel.runDirectoryBrowseButton.text=...
+ConfigurationPanel.runDirectoryLabel.text=Run Directory
+ConfigurationPanel.runArgumentsTextField.text=
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/ConfigurationPanel.form b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/ConfigurationPanel.form
new file mode 100644
index 0000000..8a4f0e0
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/ConfigurationPanel.form
@@ -0,0 +1,435 @@
+<?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.
+
+-->
+
+<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" attributes="0">
+                      <Group type="103" groupAlignment="0" attributes="0">
+                          <Component id="runExecutableLabel" min="-2" max="-2" attributes="0"/>
+                          <Component id="runArgumentsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+                          <Component id="runDirectoryLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="0" attributes="0">
+                          <Component id="runArgumentsTextField" alignment="1" max="32767" attributes="0"/>
+                          <Component id="runDirectoryTextField" max="32767" attributes="0"/>
+                          <Component id="runExecutableTextField" alignment="0" max="32767" attributes="0"/>
+                      </Group>
+                      <EmptySpace type="separate" max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="0" max="-2" attributes="0">
+                          <Component id="runExecutableBrowseButton" max="32767" attributes="0"/>
+                          <Component id="runDirectoryBrowseButton" max="32767" attributes="0"/>
+                      </Group>
+                  </Group>
+                  <Component id="jSeparator2" alignment="0" max="32767" attributes="0"/>
+                  <Component id="jScrollPane1" alignment="0" max="32767" attributes="0"/>
+                  <Group type="102" alignment="0" attributes="0">
+                      <Group type="103" groupAlignment="0" attributes="0">
+                          <Component id="buildDirectoryLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+                          <Component id="buildTypeLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+                          <Component id="backendLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+                          <Component id="configurationLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+                          <Component id="wrapModeLabel" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <Group type="103" groupAlignment="0" attributes="0">
+                          <Group type="102" attributes="0">
+                              <EmptySpace min="-2" pref="27" max="-2" attributes="0"/>
+                              <Group type="103" groupAlignment="0" attributes="0">
+                                  <Component id="buildDirectoryTextField" max="32767" attributes="0"/>
+                                  <Component id="buildTypeComboBox" alignment="0" max="32767" attributes="0"/>
+                                  <Component id="backendComboBox" max="32767" attributes="0"/>
+                                  <Component id="wrapModeComboBox" alignment="0" max="32767" attributes="0"/>
+                              </Group>
+                              <EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
+                              <Component id="buildDirectoryBrowseButton" min="-2" max="-2" attributes="0"/>
+                          </Group>
+                          <Group type="102" attributes="0">
+                              <EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
+                              <Component id="configurationComboBox" pref="190" max="32767" attributes="0"/>
+                              <EmptySpace type="separate" max="-2" attributes="0"/>
+                              <Component id="newConfigurationButton" min="-2" pref="78" max="-2" attributes="0"/>
+                              <EmptySpace type="separate" max="-2" attributes="0"/>
+                              <Component id="renameConfigurationButton" min="-2" max="-2" attributes="0"/>
+                              <EmptySpace type="separate" max="-2" attributes="0"/>
+                              <Component id="deleteConfigurationButton" min="-2" pref="103" max="-2" attributes="0"/>
+                          </Group>
+                      </Group>
+                  </Group>
+                  <Component id="jSeparator1" alignment="0" max="32767" attributes="0"/>
+              </Group>
+              <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="configurationLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="configurationComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="newConfigurationButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="deleteConfigurationButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="renameConfigurationButton" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace type="separate" max="-2" attributes="0"/>
+              <Component id="jSeparator1" min="-2" pref="10" max="-2" attributes="0"/>
+              <EmptySpace type="separate" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="buildDirectoryTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="buildDirectoryLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="buildDirectoryBrowseButton" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace type="separate" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="1" attributes="0">
+                  <Group type="102" attributes="0">
+                      <Group type="103" groupAlignment="3" attributes="0">
+                          <Component id="buildTypeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                          <Component id="buildTypeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <EmptySpace type="separate" min="-2" max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="3" attributes="0">
+                          <Component id="backendLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                          <Component id="backendComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <EmptySpace type="separate" max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="3" attributes="0">
+                          <Component id="wrapModeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                          <Component id="wrapModeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <EmptySpace type="separate" max="-2" attributes="0"/>
+                      <Component id="jScrollPane1" min="-2" pref="115" max="-2" attributes="0"/>
+                      <EmptySpace type="separate" max="-2" attributes="0"/>
+                      <Component id="jSeparator2" min="-2" pref="10" max="-2" attributes="0"/>
+                      <EmptySpace type="separate" max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="3" attributes="0">
+                          <Component id="runExecutableTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+                          <Component id="runExecutableLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <EmptySpace type="separate" max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="3" attributes="0">
+                          <Component id="runArgumentsTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+                          <Component id="runArgumentsLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <EmptySpace type="separate" max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="3" attributes="0">
+                          <Component id="runDirectoryTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+                          <Component id="runDirectoryLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                  </Group>
+                  <Group type="102" attributes="0">
+                      <Component id="runExecutableBrowseButton" min="-2" max="-2" attributes="0"/>
+                      <EmptySpace min="70" pref="70" max="-2" attributes="0"/>
+                      <Component id="runDirectoryBrowseButton" min="-2" max="-2" attributes="0"/>
+                  </Group>
+              </Group>
+              <EmptySpace pref="20" max="32767" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JTextField" name="buildDirectoryTextField">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.buildDirectoryTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+        <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[32767, 32767]"/>
+        </Property>
+        <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[160, 34]"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JLabel" name="buildDirectoryLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.buildDirectoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JButton" name="buildDirectoryBrowseButton">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.buildDirectoryBrowseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="buildDirectoryBrowseButtonActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="buildTypeLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.buildTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="buildTypeComboBox">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+          <StringArray count="6">
+            <StringItem index="0" value="plain"/>
+            <StringItem index="1" value="debug"/>
+            <StringItem index="2" value="debugoptimized"/>
+            <StringItem index="3" value="release"/>
+            <StringItem index="4" value="minsize"/>
+            <StringItem index="5" value="custom"/>
+          </StringArray>
+        </Property>
+        <Property name="selectedIndex" type="int" value="1"/>
+        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[80, 34]"/>
+        </Property>
+        <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[160, 34]"/>
+        </Property>
+      </Properties>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+      </AuxValues>
+    </Component>
+    <Component class="javax.swing.JLabel" name="wrapModeLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.wrapModeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="wrapModeComboBox">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+          <StringArray count="5">
+            <StringItem index="0" value="default"/>
+            <StringItem index="1" value="nofallback"/>
+            <StringItem index="2" value="nodownload"/>
+            <StringItem index="3" value="forcefallback"/>
+            <StringItem index="4" value="nopromote"/>
+          </StringArray>
+        </Property>
+        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[80, 34]"/>
+        </Property>
+      </Properties>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+      </AuxValues>
+    </Component>
+    <Component class="javax.swing.JLabel" name="backendLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.backendLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="backendComboBox">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+          <StringArray count="11">
+            <StringItem index="0" value="ninja"/>
+            <StringItem index="1" value="vs"/>
+            <StringItem index="2" value="vs2010"/>
+            <StringItem index="3" value="vs2012"/>
+            <StringItem index="4" value="vs2013"/>
+            <StringItem index="5" value="vs2015"/>
+            <StringItem index="6" value="vs2017"/>
+            <StringItem index="7" value="vs2019"/>
+            <StringItem index="8" value="vs2022"/>
+            <StringItem index="9" value="xcode"/>
+            <StringItem index="10" value="none"/>
+          </StringArray>
+        </Property>
+        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[80, 34]"/>
+        </Property>
+        <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[160, 34]"/>
+        </Property>
+      </Properties>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+      </AuxValues>
+    </Component>
+    <Component class="javax.swing.JLabel" name="configurationLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.configurationLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="configurationComboBox">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+          <Connection code="configurationComboBoxModel" type="code"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="configurationComboBoxItemStateChanged"/>
+      </Events>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+      </AuxValues>
+    </Component>
+    <Component class="javax.swing.JButton" name="newConfigurationButton">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.newConfigurationButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newConfigurationButtonActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JButton" name="deleteConfigurationButton">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.deleteConfigurationButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteConfigurationButtonActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JButton" name="renameConfigurationButton">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.renameConfigurationButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="renameConfigurationButtonActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JSeparator" name="jSeparator1">
+    </Component>
+    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+      <AuxValues>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTable" name="additionalArgumentsTable">
+          <Properties>
+            <Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
+              <Table columnCount="2" rowCount="3">
+                <Column editable="false" title="Command" type="java.lang.String">
+                  <Data value="setup"/>
+                  <Data value="compile"/>
+                  <Data value="test"/>
+                </Column>
+                <Column editable="true" title="Additional Arguments" type="java.lang.String"/>
+              </Table>
+            </Property>
+            <Property name="autoResizeMode" type="int" value="3"/>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="additionalArgumentsTable.getColumnModel().getColumn(0).setMinWidth(100);&#xa;additionalArgumentsTable.getColumnModel().getColumn(0).setMaxWidth(150);"/>
+          </AuxValues>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Component class="javax.swing.JSeparator" name="jSeparator2">
+    </Component>
+    <Component class="javax.swing.JLabel" name="runExecutableLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.runExecutableLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JTextField" name="runExecutableTextField">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.runExecutableTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JButton" name="runExecutableBrowseButton">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.runExecutableBrowseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="runExecutableBrowseButtonActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="runArgumentsLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.runArgumentsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JTextField" name="runArgumentsTextField">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.runArgumentsTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JLabel" name="runDirectoryLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.runDirectoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JButton" name="runDirectoryBrowseButton">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.runDirectoryBrowseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="runDirectoryBrowseButtonActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JTextField" name="runDirectoryTextField">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/cnd/meson/project/ui/Bundle.properties" key="ConfigurationPanel.runDirectoryTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+  </SubComponents>
+</Form>
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/ConfigurationPanel.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/ConfigurationPanel.java
new file mode 100644
index 0000000..41cd95f
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/ConfigurationPanel.java
@@ -0,0 +1,688 @@
+/*
+ * 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.meson.project.ui;
+
+import java.awt.event.ItemEvent;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.ComboBoxModel;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import static org.netbeans.modules.cnd.meson.project.ActionProviderImpl.COMMAND_SETUP;
+import org.netbeans.modules.cnd.meson.project.Configuration;
+import org.netbeans.modules.cnd.meson.project.ConfigurationProvider;
+import org.netbeans.modules.cnd.meson.project.MesonProject;
+import static org.netbeans.spi.project.ActionProvider.COMMAND_BUILD;
+import static org.netbeans.spi.project.ActionProvider.COMMAND_TEST;
+import org.netbeans.spi.project.ui.support.ProjectCustomizer;
+import org.openide.util.Lookup;
+
+
+/**
+ *
+ */
+public class ConfigurationPanel extends javax.swing.JPanel
+{
+
+    /**
+     * Creates new form MesonProjectOptionsPanel
+     */
+    public ConfigurationPanel(String projectRoot)
+    {
+        this.projectRoot = projectRoot;
+        initComponents();
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents()
+    {
+
+        buildDirectoryTextField = new javax.swing.JTextField();
+        buildDirectoryLabel = new javax.swing.JLabel();
+        buildDirectoryBrowseButton = new javax.swing.JButton();
+        buildTypeLabel = new javax.swing.JLabel();
+        buildTypeComboBox = new javax.swing.JComboBox<>();
+        wrapModeLabel = new javax.swing.JLabel();
+        wrapModeComboBox = new javax.swing.JComboBox<>();
+        backendLabel = new javax.swing.JLabel();
+        backendComboBox = new javax.swing.JComboBox<>();
+        configurationLabel = new javax.swing.JLabel();
+        configurationComboBox = new javax.swing.JComboBox<>();
+        newConfigurationButton = new javax.swing.JButton();
+        deleteConfigurationButton = new javax.swing.JButton();
+        renameConfigurationButton = new javax.swing.JButton();
+        jSeparator1 = new javax.swing.JSeparator();
+        jScrollPane1 = new javax.swing.JScrollPane();
+        additionalArgumentsTable = new javax.swing.JTable();
+        jSeparator2 = new javax.swing.JSeparator();
+        runExecutableLabel = new javax.swing.JLabel();
+        runExecutableTextField = new javax.swing.JTextField();
+        runExecutableBrowseButton = new javax.swing.JButton();
+        runArgumentsLabel = new javax.swing.JLabel();
+        runArgumentsTextField = new javax.swing.JTextField();
+        runDirectoryLabel = new javax.swing.JLabel();
+        runDirectoryBrowseButton = new javax.swing.JButton();
+        runDirectoryTextField = new javax.swing.JTextField();
+
+        buildDirectoryTextField.setText(org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.buildDirectoryTextField.text")); // NOI18N
+        buildDirectoryTextField.setMaximumSize(new java.awt.Dimension(32767, 32767));
+        buildDirectoryTextField.setPreferredSize(new java.awt.Dimension(160, 34));
+
+        org.openide.awt.Mnemonics.setLocalizedText(buildDirectoryLabel, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.buildDirectoryLabel.text")); // NOI18N
+
+        org.openide.awt.Mnemonics.setLocalizedText(buildDirectoryBrowseButton, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.buildDirectoryBrowseButton.text")); // NOI18N
+        buildDirectoryBrowseButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                buildDirectoryBrowseButtonActionPerformed(evt);
+            }
+        });
+
+        org.openide.awt.Mnemonics.setLocalizedText(buildTypeLabel, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.buildTypeLabel.text")); // NOI18N
+
+        buildTypeComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "plain", "debug", "debugoptimized", "release", "minsize", "custom" }));
+        buildTypeComboBox.setSelectedIndex(1);
+        buildTypeComboBox.setMinimumSize(new java.awt.Dimension(80, 34));
+        buildTypeComboBox.setPreferredSize(new java.awt.Dimension(160, 34));
+
+        org.openide.awt.Mnemonics.setLocalizedText(wrapModeLabel, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.wrapModeLabel.text")); // NOI18N
+
+        wrapModeComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "default", "nofallback", "nodownload", "forcefallback", "nopromote" }));
+        wrapModeComboBox.setMinimumSize(new java.awt.Dimension(80, 34));
+
+        org.openide.awt.Mnemonics.setLocalizedText(backendLabel, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.backendLabel.text")); // NOI18N
+
+        backendComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "ninja", "vs", "vs2010", "vs2012", "vs2013", "vs2015", "vs2017", "vs2019", "vs2022", "xcode", "none" }));
+        backendComboBox.setMinimumSize(new java.awt.Dimension(80, 34));
+        backendComboBox.setPreferredSize(new java.awt.Dimension(160, 34));
+
+        org.openide.awt.Mnemonics.setLocalizedText(configurationLabel, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.configurationLabel.text")); // NOI18N
+
+        configurationComboBox.setModel(configurationComboBoxModel);
+        configurationComboBox.addItemListener(new java.awt.event.ItemListener()
+        {
+            public void itemStateChanged(java.awt.event.ItemEvent evt)
+            {
+                configurationComboBoxItemStateChanged(evt);
+            }
+        });
+
+        org.openide.awt.Mnemonics.setLocalizedText(newConfigurationButton, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.newConfigurationButton.text")); // NOI18N
+        newConfigurationButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                newConfigurationButtonActionPerformed(evt);
+            }
+        });
+
+        org.openide.awt.Mnemonics.setLocalizedText(deleteConfigurationButton, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.deleteConfigurationButton.text")); // NOI18N
+        deleteConfigurationButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                deleteConfigurationButtonActionPerformed(evt);
+            }
+        });
+
+        org.openide.awt.Mnemonics.setLocalizedText(renameConfigurationButton, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.renameConfigurationButton.text")); // NOI18N
+        renameConfigurationButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                renameConfigurationButtonActionPerformed(evt);
+            }
+        });
+
+        additionalArgumentsTable.setModel(new javax.swing.table.DefaultTableModel(
+            new Object [][]
+            {
+                {"setup", null},
+                {"compile", null},
+                {"test", null}
+            },
+            new String []
+            {
+                "Command", "Additional Arguments"
+            }
+        )
+        {
+            Class[] types = new Class []
+            {
+                java.lang.String.class, java.lang.String.class
+            };
+            boolean[] canEdit = new boolean []
+            {
+                false, true
+            };
+
+            public Class getColumnClass(int columnIndex)
+            {
+                return types [columnIndex];
+            }
+
+            public boolean isCellEditable(int rowIndex, int columnIndex)
+            {
+                return canEdit [columnIndex];
+            }
+        });
+        additionalArgumentsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_LAST_COLUMN);
+        additionalArgumentsTable.getColumnModel().getColumn(0).setMinWidth(100);
+        additionalArgumentsTable.getColumnModel().getColumn(0).setMaxWidth(150);
+        jScrollPane1.setViewportView(additionalArgumentsTable);
+
+        org.openide.awt.Mnemonics.setLocalizedText(runExecutableLabel, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.runExecutableLabel.text")); // NOI18N
+
+        runExecutableTextField.setText(org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.runExecutableTextField.text")); // NOI18N
+
+        org.openide.awt.Mnemonics.setLocalizedText(runExecutableBrowseButton, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.runExecutableBrowseButton.text")); // NOI18N
+        runExecutableBrowseButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                runExecutableBrowseButtonActionPerformed(evt);
+            }
+        });
+
+        org.openide.awt.Mnemonics.setLocalizedText(runArgumentsLabel, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.runArgumentsLabel.text")); // NOI18N
+
+        runArgumentsTextField.setText(org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.runArgumentsTextField.text")); // NOI18N
+
+        org.openide.awt.Mnemonics.setLocalizedText(runDirectoryLabel, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.runDirectoryLabel.text")); // NOI18N
+
+        org.openide.awt.Mnemonics.setLocalizedText(runDirectoryBrowseButton, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.runDirectoryBrowseButton.text")); // NOI18N
+        runDirectoryBrowseButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                runDirectoryBrowseButtonActionPerformed(evt);
+            }
+        });
+
+        runDirectoryTextField.setText(org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.runDirectoryTextField.text")); // NOI18N
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addGap(20, 20, 20)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(layout.createSequentialGroup()
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addComponent(runExecutableLabel)
+                            .addComponent(runArgumentsLabel)
+                            .addComponent(runDirectoryLabel))
+                        .addGap(21, 21, 21)
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addComponent(runArgumentsTextField, javax.swing.GroupLayout.Alignment.TRAILING)
+                            .addComponent(runDirectoryTextField)
+                            .addComponent(runExecutableTextField))
+                        .addGap(18, 18, 18)
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+                            .addComponent(runExecutableBrowseButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                            .addComponent(runDirectoryBrowseButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+                    .addComponent(jSeparator2)
+                    .addComponent(jScrollPane1)
+                    .addGroup(layout.createSequentialGroup()
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addComponent(buildDirectoryLabel)
+                            .addComponent(buildTypeLabel)
+                            .addComponent(backendLabel)
+                            .addComponent(configurationLabel)
+                            .addComponent(wrapModeLabel))
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addGroup(layout.createSequentialGroup()
+                                .addGap(27, 27, 27)
+                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                                    .addComponent(buildDirectoryTextField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                    .addComponent(buildTypeComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                    .addComponent(backendComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                    .addComponent(wrapModeComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                                .addGap(18, 18, 18)
+                                .addComponent(buildDirectoryBrowseButton))
+                            .addGroup(layout.createSequentialGroup()
+                                .addGap(26, 26, 26)
+                                .addComponent(configurationComboBox, 0, 190, Short.MAX_VALUE)
+                                .addGap(18, 18, 18)
+                                .addComponent(newConfigurationButton, javax.swing.GroupLayout.PREFERRED_SIZE, 78, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                .addGap(18, 18, 18)
+                                .addComponent(renameConfigurationButton)
+                                .addGap(18, 18, 18)
+                                .addComponent(deleteConfigurationButton, javax.swing.GroupLayout.PREFERRED_SIZE, 103, javax.swing.GroupLayout.PREFERRED_SIZE))))
+                    .addComponent(jSeparator1))
+                .addGap(20, 20, 20))
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addGap(20, 20, 20)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(configurationLabel)
+                    .addComponent(configurationComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(newConfigurationButton)
+                    .addComponent(deleteConfigurationButton)
+                    .addComponent(renameConfigurationButton))
+                .addGap(18, 18, 18)
+                .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addGap(18, 18, 18)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(buildDirectoryTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(buildDirectoryLabel)
+                    .addComponent(buildDirectoryBrowseButton))
+                .addGap(18, 18, 18)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+                    .addGroup(layout.createSequentialGroup()
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                            .addComponent(buildTypeLabel)
+                            .addComponent(buildTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                        .addGap(18, 18, 18)
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                            .addComponent(backendLabel)
+                            .addComponent(backendComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                        .addGap(18, 18, 18)
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                            .addComponent(wrapModeLabel)
+                            .addComponent(wrapModeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                        .addGap(18, 18, 18)
+                        .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 115, javax.swing.GroupLayout.PREFERRED_SIZE)
+                        .addGap(18, 18, 18)
+                        .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
+                        .addGap(18, 18, 18)
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                            .addComponent(runExecutableTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                            .addComponent(runExecutableLabel))
+                        .addGap(18, 18, 18)
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                            .addComponent(runArgumentsTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                            .addComponent(runArgumentsLabel))
+                        .addGap(18, 18, 18)
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                            .addComponent(runDirectoryTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                            .addComponent(runDirectoryLabel)))
+                    .addGroup(layout.createSequentialGroup()
+                        .addComponent(runExecutableBrowseButton)
+                        .addGap(70, 70, 70)
+                        .addComponent(runDirectoryBrowseButton)))
+                .addContainerGap(20, Short.MAX_VALUE))
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void renameConfigurationButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_renameConfigurationButtonActionPerformed
+    {//GEN-HEADEREND:event_renameConfigurationButtonActionPerformed
+        final String oldName = configurationComboBox.getSelectedItem().toString();
+
+        String newName =
+            (String)JOptionPane.showInputDialog(
+                this,
+                "Please enter a new name for the \"" + oldName + "\" configuration.",
+                "Rename Configuration",
+                JOptionPane.PLAIN_MESSAGE,
+                null,
+                null,
+                oldName);
+
+        if ((newName != null) && !newName.equals(oldName)) {
+            boolean alreadyExists = false;
+            for (Configuration cfg: configurations) {
+                if (newName.equals(cfg.getDisplayName())) {
+                    alreadyExists = true;
+                    break;
+                }
+            }
+            if (!alreadyExists) {
+                for (Configuration cfg: configurations) {
+                    if (oldName.equals(cfg.getDisplayName())) {
+                        cfg.setDisplayName(newName);
+                        if (cfg.getBuildDirectory().equals(Paths.get(MesonProject.BUILD_DIRECTORY, oldName).toString())) {
+                            cfg.setBuildDirectory(Paths.get(MesonProject.BUILD_DIRECTORY, newName).toString());
+                            buildDirectoryTextField.setText(cfg.getBuildDirectory());
+                        }
+                        configurationComboBoxModel.setSelectedItem(newName);
+                        configurationComboBoxModel.configurationsChanged();
+                        break;
+                    }
+                }
+            }
+            else {
+                JOptionPane.showMessageDialog(
+                    this,
+                    "Failed to rename configuration because a configuration named \"" + newName + "\" already exsits.",
+                    "Rename Configuration Failed",
+                    JOptionPane.ERROR_MESSAGE);
+            }
+        }
+    }//GEN-LAST:event_renameConfigurationButtonActionPerformed
+
+    private void buildDirectoryBrowseButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_buildDirectoryBrowseButtonActionPerformed
+    {//GEN-HEADEREND:event_buildDirectoryBrowseButtonActionPerformed
+        JFileChooser chooser = new JFileChooser();
+        chooser.setSelectedFile(Paths.get(projectRoot, buildDirectoryTextField.getText()).toFile());
+        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+        chooser.setFileHidingEnabled(false);
+        final int result = chooser.showDialog(this, "Select");
+        if (result == JFileChooser.APPROVE_OPTION) {
+            buildDirectoryTextField.setText(
+                Paths.get(projectRoot).relativize(chooser.getSelectedFile().toPath()).toString());
+        }
+    }//GEN-LAST:event_buildDirectoryBrowseButtonActionPerformed
+
+    private void configurationComboBoxItemStateChanged(java.awt.event.ItemEvent evt)//GEN-FIRST:event_configurationComboBoxItemStateChanged
+    {//GEN-HEADEREND:event_configurationComboBoxItemStateChanged
+        if (evt.getStateChange() == ItemEvent.SELECTED) {
+            final String selectedCfg = evt.getItem().toString();
+
+            for (Configuration cfg: configurations) {
+                if (selectedCfg.equals(cfg.getDisplayName())) {
+                    toUI(cfg);
+                    break;
+                }
+            }
+        }
+        else if (evt.getStateChange() == ItemEvent.DESELECTED) {
+            final String selectedCfg = evt.getItem().toString();
+
+            for (Configuration cfg: configurations) {
+                if (selectedCfg.equals(cfg.getDisplayName())) {
+                    fromUI(cfg);
+                    break;
+                }
+            }
+        }
+    }//GEN-LAST:event_configurationComboBoxItemStateChanged
+
+    private void newConfigurationButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_newConfigurationButtonActionPerformed
+    {//GEN-HEADEREND:event_newConfigurationButtonActionPerformed
+        String cfgName =
+            JOptionPane.showInputDialog(
+                this,
+                "Please enter a name for the new configuration.",
+                "New Configuration",
+                JOptionPane.PLAIN_MESSAGE);
+
+        if (cfgName != null) {
+            boolean alreadyExists = false;
+            for (Configuration cfg: configurations) {
+                if (cfgName.equals(cfg.getDisplayName())) {
+                    alreadyExists = true;
+                    break;
+                }
+            }
+            if (!alreadyExists) {
+                Configuration cfg = Configuration.getDefault();
+                cfg.setDisplayName(cfgName);
+                cfg.setBuildDirectory(Paths.get(MesonProject.BUILD_DIRECTORY, cfgName).toString());
+                configurations.add(cfg);
+                configurationComboBoxModel.configurationsChanged();
+                configurationComboBox.setSelectedItem(cfgName);
+            }
+            else {
+                JOptionPane.showMessageDialog(
+                    this,
+                    "Failed to create new configuration because a configuration named \"" + cfgName + "\" already exsits.",
+                    "Create Configuration Failed",
+                    JOptionPane.ERROR_MESSAGE);
+            }
+        }
+    }//GEN-LAST:event_newConfigurationButtonActionPerformed
+
+    private void deleteConfigurationButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_deleteConfigurationButtonActionPerformed
+    {//GEN-HEADEREND:event_deleteConfigurationButtonActionPerformed
+        final String cfgName = configurationComboBox.getSelectedItem().toString();
+
+        if (configurations.size() > 1) {
+            for (Configuration cfg: configurations) {
+                if (cfgName.equals(cfg.getDisplayName())) {
+                    configurations.remove(cfg);
+                    configurationComboBoxModel.setSelectedItem(configurations.get(0).getDisplayName());
+                    configurationComboBoxModel.configurationsChanged();
+                    toUI(configurations.get(0));
+                }
+            }
+        }
+        else {
+            JOptionPane.showMessageDialog(
+                this,
+                "Cannot delete the \"" + cfgName + "\" configuration because it's the last one and at least one configuration must exist.",
+                "Delete Configuration Failed",
+                JOptionPane.ERROR_MESSAGE);
+        }
+    }//GEN-LAST:event_deleteConfigurationButtonActionPerformed
+
+    private void runExecutableBrowseButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_runExecutableBrowseButtonActionPerformed
+    {//GEN-HEADEREND:event_runExecutableBrowseButtonActionPerformed
+        JFileChooser chooser = new JFileChooser();
+
+        if (runExecutableTextField.getText().isEmpty()) {
+            chooser.setCurrentDirectory(Paths.get(projectRoot, buildDirectoryTextField.getText()).toFile());
+        }
+        else {
+            chooser.setSelectedFile(Paths.get(projectRoot, runExecutableTextField.getText()).toFile());
+        }
+        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+        chooser.setFileHidingEnabled(false);
+        final int result = chooser.showDialog(this, "Select");
+        if (result == JFileChooser.APPROVE_OPTION) {
+            runExecutableTextField.setText(
+                Paths.get(projectRoot).relativize(chooser.getSelectedFile().toPath()).toString());
+        }
+    }//GEN-LAST:event_runExecutableBrowseButtonActionPerformed
+
+    private void runDirectoryBrowseButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_runDirectoryBrowseButtonActionPerformed
+    {//GEN-HEADEREND:event_runDirectoryBrowseButtonActionPerformed
+        JFileChooser chooser = new JFileChooser();
+        if (runDirectoryTextField.getText().isEmpty()) {
+            chooser.setCurrentDirectory(Paths.get(projectRoot).toFile());
+        }
+        else {
+            chooser.setSelectedFile(Paths.get(projectRoot, runDirectoryTextField.getText()).toFile());
+        }
+        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+        chooser.setFileHidingEnabled(false);
+        final int result = chooser.showDialog(this, "Select");
+        if (result == JFileChooser.APPROVE_OPTION) {
+            runDirectoryTextField.setText(
+                Paths.get(projectRoot).relativize(chooser.getSelectedFile().toPath()).toString());
+        }
+    }//GEN-LAST:event_runDirectoryBrowseButtonActionPerformed
+
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JTable additionalArgumentsTable;
+    private javax.swing.JComboBox<String> backendComboBox;
+    private javax.swing.JLabel backendLabel;
+    private javax.swing.JButton buildDirectoryBrowseButton;
+    private javax.swing.JLabel buildDirectoryLabel;
+    private javax.swing.JTextField buildDirectoryTextField;
+    private javax.swing.JComboBox<String> buildTypeComboBox;
+    private javax.swing.JLabel buildTypeLabel;
+    private javax.swing.JComboBox<String> configurationComboBox;
+    private javax.swing.JLabel configurationLabel;
+    private javax.swing.JButton deleteConfigurationButton;
+    private javax.swing.JScrollPane jScrollPane1;
+    private javax.swing.JSeparator jSeparator1;
+    private javax.swing.JSeparator jSeparator2;
+    private javax.swing.JButton newConfigurationButton;
+    private javax.swing.JButton renameConfigurationButton;
+    private javax.swing.JLabel runArgumentsLabel;
+    private javax.swing.JTextField runArgumentsTextField;
+    private javax.swing.JButton runDirectoryBrowseButton;
+    private javax.swing.JLabel runDirectoryLabel;
+    private javax.swing.JTextField runDirectoryTextField;
+    private javax.swing.JButton runExecutableBrowseButton;
+    private javax.swing.JLabel runExecutableLabel;
+    private javax.swing.JTextField runExecutableTextField;
+    private javax.swing.JComboBox<String> wrapModeComboBox;
+    private javax.swing.JLabel wrapModeLabel;
+    // End of variables declaration//GEN-END:variables
+
+    @ProjectCustomizer.CompositeCategoryProvider.Registration(projectType=MesonProject.PROJECT_KEY, position=220)
+    public static ProjectCustomizer.CompositeCategoryProvider createCategoryProvider() {
+        return new ProjectCustomizer.CompositeCategoryProvider() {
+            @Override
+            public ProjectCustomizer.Category createCategory(Lookup context) {
+                return ProjectCustomizer.Category.create("configuration", "Configuration", null);
+            }
+            @Override
+            public JComponent createComponent(ProjectCustomizer.Category category, Lookup context) {
+                MesonProject project = context.lookup(MesonProject.class);
+                ConfigurationPanel panel = new ConfigurationPanel(project.getProjectDirectory().getPath());
+                panel.load(project.getConfigurationProvider());
+                category.setOkButtonListener(evt -> {
+                    panel.commit(project.getConfigurationProvider());
+                });
+                category.setStoreListener(evt -> {
+                    project.getConfigurationProvider().save();
+                });
+                return panel;
+            }
+        };
+    }
+
+    private final String projectRoot;
+    private List<Configuration> configurations = new ArrayList<>();
+
+    private final class ConfigurationComboBoxModel implements ComboBoxModel<String> {
+        private String selectedItem = null;
+        private ListDataListener listener = null;
+
+        @Override
+        public void setSelectedItem(Object o) {
+            selectedItem = o.toString();
+        }
+
+        @Override
+        public Object getSelectedItem() {
+            return selectedItem;
+        }
+
+        @Override
+        public int getSize() {
+            return configurations.size();
+        }
+
+        @Override
+        public String getElementAt(int i) {
+            return configurations.get(i).getDisplayName();
+        }
+
+        @Override
+        public void addListDataListener(ListDataListener ll) {
+            listener = ll;
+        }
+
+        @Override
+        public void removeListDataListener(ListDataListener ll) {
+            if (listener == ll) {
+                listener = null;
+            }
+        }
+
+        public void configurationsChanged() {
+            if (selectedItem != null) {
+                boolean found = false;
+                for (Configuration cfg: configurations) {
+                    if (selectedItem.equals(cfg.getDisplayName())) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    selectedItem = null;
+                }
+            }
+
+            if (listener != null) {
+                listener.contentsChanged(
+                    new ListDataEvent(
+                        this,
+                        ListDataEvent.CONTENTS_CHANGED,
+                        0,
+                        getSize() - 1));
+            }
+
+            if ((selectedItem == null) && !configurations.isEmpty()) {
+                configurationComboBox.setSelectedItem(
+                    configurations.get(0).getDisplayName());
+            }
+        }
+    }
+
+    private ConfigurationComboBoxModel configurationComboBoxModel = new ConfigurationComboBoxModel();
+
+    private void load(ConfigurationProvider provider) {
+        for (Configuration cfg: provider.getConfigurations()) {
+            configurations.add(new Configuration(cfg));
+        }
+        configurationComboBoxModel.configurationsChanged();
+        configurationComboBox.setSelectedItem(
+            provider.getActiveConfiguration().getDisplayName());
+    }
+
+    private void commit(ConfigurationProvider provider) {
+        final String selectedCfg = configurationComboBox.getSelectedItem().toString();
+
+        for (Configuration cfg: configurations) {
+            if (selectedCfg.equals(cfg.getDisplayName())) {
+                fromUI(cfg);
+                break;
+            }
+        }
+
+        provider.setConfigurations(configurations);
+    }
+
+    private void toUI(Configuration cfg) {
+        buildDirectoryTextField.setText(cfg.getBuildDirectory());
+        buildTypeComboBox.setSelectedItem(cfg.getBuildType());
+        backendComboBox.setSelectedItem(cfg.getBackend());
+        wrapModeComboBox.setSelectedItem(cfg.getWrapMode());
+        additionalArgumentsTable.setValueAt(cfg.getAdditionalArgumentsFor(COMMAND_SETUP), 0, 1);
+        additionalArgumentsTable.setValueAt(cfg.getAdditionalArgumentsFor(COMMAND_BUILD), 1, 1);
+        additionalArgumentsTable.setValueAt(cfg.getAdditionalArgumentsFor(COMMAND_TEST), 2, 1);
+        runExecutableTextField.setText(cfg.getRunExecutable());
+        runArgumentsTextField.setText(cfg.getRunArguments());
+        runDirectoryTextField.setText(cfg.getRunDirectory());
+    }
+
+    private void fromUI(Configuration cfg) {
+        cfg.setBuildDirectory(buildDirectoryTextField.getText());
+        cfg.setBuildType(buildTypeComboBox.getSelectedItem().toString());
+        cfg.setBackend(backendComboBox.getSelectedItem().toString());
+        cfg.setWrapMode(wrapModeComboBox.getSelectedItem().toString());
+        cfg.setAdditionalArgumentsFor(COMMAND_SETUP, additionalArgumentsTable.getValueAt(0, 1).toString());
+        cfg.setAdditionalArgumentsFor(COMMAND_BUILD, additionalArgumentsTable.getValueAt(1, 1).toString());
+        cfg.setAdditionalArgumentsFor(COMMAND_TEST, additionalArgumentsTable.getValueAt(2, 1).toString());
+        cfg.setRunExecutable(runExecutableTextField.getText());
+        cfg.setRunArguments(runArgumentsTextField.getText());
+        cfg.setRunDirectory(runDirectoryTextField.getText());
+    }
+}
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/CustomizerProviderImpl.java b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/CustomizerProviderImpl.java
new file mode 100644
index 0000000..b1e9363
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/project/ui/CustomizerProviderImpl.java
@@ -0,0 +1,48 @@
+/*
+ * 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.meson.project.ui;
+
+import org.netbeans.modules.cnd.meson.project.MesonProject;
+import org.netbeans.spi.project.ui.CustomizerProvider2;
+import org.netbeans.spi.project.ui.support.ProjectCustomizer;
+import org.openide.util.HelpCtx;
+
+public class CustomizerProviderImpl implements CustomizerProvider2 {
+
+    private final MesonProject project;
+
+    public CustomizerProviderImpl(MesonProject project) {
+        this.project = project;
+    }
+
+    @Override
+    public void showCustomizer(String preselectedCategory, String preselectedSubCategory) {
+        ProjectCustomizer.createCustomizerDialog(
+            "Projects/" + MesonProject.PROJECT_KEY + "/Customizer",
+            project.getLookup(),
+            preselectedCategory,
+            evt -> {},
+            HelpCtx.DEFAULT_HELP).setVisible(true);
+    }
+
+    @Override
+    public void showCustomizer() {
+        showCustomizer(null, null);
+    }
+}
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/file_icon.png b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/file_icon.png
new file mode 100644
index 0000000..c73a2cc
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/file_icon.png
Binary files differ
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/fonts_and_colors.xml b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/fonts_and_colors.xml
new file mode 100644
index 0000000..3797679
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/fonts_and_colors.xml
@@ -0,0 +1,34 @@
+<?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.
+
+-->
+<!DOCTYPE fontscolors PUBLIC "-//NetBeans//DTD Editor Fonts and Colors settings 1.1//EN" "http://www.netbeans.org/dtds/EditorFontsColors-1_1.dtd">
+<fontscolors>
+    <fontcolor default="error" name="error"/>
+    <fontcolor default="keyword" name="keyword"/>
+    <fontcolor default="whitespace" name="whitespace"/>
+    <fontcolor default="number" name="number"/>
+    <fontcolor default="operator" name="operator"/>
+    <fontcolor default="string" name="string"/>
+    <fontcolor default="keyword" name="command"/>
+    <fontcolor default="comment" name="comment"/>
+    <fontcolor default="identifier" name="identifier"/>
+    <fontcolor default="literal" name="literal"/>
+</fontscolors>
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/layer.xml b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/layer.xml
new file mode 100644
index 0000000..1a85ba9
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/layer.xml
@@ -0,0 +1,183 @@
+<?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.
+
+-->
+<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
+<filesystem>
+    <folder name="Editors">
+        <folder name="text">
+            <folder name="x-meson-build">
+                <file name="language.instance">
+                    <attr name="instanceCreate" methodvalue="org.netbeans.modules.cnd.meson.lexer.MesonBuildTokenId.language"/>
+                    <attr name="instanceOf" stringvalue="org.netbeans.api.lexer.Language"/>
+                </file>
+                <folder name="FontsColors">
+                    <folder name="NetBeans">
+                        <folder name="Defaults">
+                            <file name="fontsColors.xml" url="fonts_and_colors.xml">
+                                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.cnd.meson.editor.Bundle"/>
+                            </file>
+                        </folder>
+                    </folder>
+                </folder>
+                <file name="EditorKit.instance">
+                    <attr name="instanceClass" stringvalue="org.netbeans.modules.cnd.meson.editor.MesonBuildKit"/>
+                    <attr name="instanceOf" stringvalue="javax.swing.text.EditorKit"/>
+                </file>
+            </folder>
+            <folder name="x-meson-options">
+                <file name="language.instance">
+                    <attr name="instanceCreate" methodvalue="org.netbeans.modules.cnd.meson.lexer.MesonOptionsTokenId.language"/>
+                    <attr name="instanceOf" stringvalue="org.netbeans.api.lexer.Language"/>
+                </file>
+                <folder name="FontsColors">
+                    <folder name="NetBeans">
+                        <folder name="Defaults">
+                            <file name="fontsColors.xml" url="fonts_and_colors.xml">
+                                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.cnd.meson.editor.Bundle"/>
+                            </file>
+                        </folder>
+                    </folder>
+                </folder>
+                <file name="EditorKit.instance">
+                    <attr name="instanceClass" stringvalue="org.netbeans.modules.cnd.meson.editor.MesonOptionsKit"/>
+                    <attr name="instanceOf" stringvalue="javax.swing.text.EditorKit"/>
+                </file>
+            </folder>
+        </folder>
+    </folder>
+    <folder name="Projects">
+        <folder name="org-netbeans-modules-cnd-meson-project">
+            <folder name="Actions">
+                <file name="org-netbeans-modules-project-ui-NewFile$WithSubMenu.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-NewFile$WithSubMenu.instance"/>
+                    <attr name="position" intvalue="100"/>
+                </file>
+                <file name="sep-1.instance">
+                    <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
+                    <attr name="position" intvalue="200"/>
+                </file>
+                <file name="org-netbeans-modules-cnd-meson-project-SetupAction.instance">
+                    <attr name="instanceCreate" methodvalue="org.netbeans.modules.cnd.meson.project.ActionProviderImpl.createSetupAction"/>
+                    <attr name="position" intvalue="250"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-BuildProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-BuildProject.instance"/>
+                    <attr name="position" intvalue="300"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-RebuildProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-RebuildProject.instance"/>
+                    <attr name="position" intvalue="400"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-CleanProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-CleanProject.instance"/>
+                    <attr name="position" intvalue="600"/>
+                </file>
+                <file name="sep-2.instance">
+                    <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
+                    <attr name="position" intvalue="800"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-TestProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-TestProject.instance"/>
+                    <attr name="position" intvalue="850"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-RunProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-RunProject.instance"/>
+                    <attr name="position" intvalue="900"/>
+                </file>
+                <file name="org-netbeans-modules-debugger-ui-actions-DebugProjectAction.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Debug/org-netbeans-modules-debugger-ui-actions-DebugProjectAction.instance"/>
+                    <attr name="position" intvalue="1000"/>
+                </file>
+                <file name="sep-3.instance">
+                    <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
+                    <attr name="position" intvalue="1300"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-actions-ActiveConfigAction.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-actions-ActiveConfigAction.instance"/>
+                    <attr name="position" intvalue="1500"/>
+                </file>
+                <file name="sep-4.instance">
+                    <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
+                    <attr name="position" intvalue="1600"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-SetMainProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-SetMainProject.instance"/>
+                    <attr name="position" intvalue="1800"/>
+                </file>
+<!--                <file name="org-netbeans-modules-project-ui-actions-OpenSubprojects.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-actions-OpenSubprojects.instance"/>
+                    <attr name="position" intvalue="1900"/>
+                </file>-->
+                <file name="org-netbeans-modules-project-ui-CloseProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-CloseProject.instance"/>
+                    <attr name="position" intvalue="2100"/>
+                </file>
+                <file name="sep-5.instance">
+                    <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
+                    <attr name="position" intvalue="2200"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-RenameProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-RenameProject.instance"/>
+                    <attr name="position" intvalue="2300"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-MoveProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-MoveProject.instance"/>
+                    <attr name="position" intvalue="2400"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-CopyProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-CopyProject.instance"/>
+                    <attr name="position" intvalue="2500"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-DeleteProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-DeleteProject.instance"/>
+                    <attr name="position" intvalue="2600"/>
+                </file>
+                <file name="sep-6.instance">
+                    <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
+                    <attr name="position" intvalue="2700"/>
+                </file>
+                <file name="org-openide-actions-FindAction.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Edit/org-openide-actions-FindAction.instance"/>
+                    <attr name="position" intvalue="2800"/>
+                </file>
+                <file name="general.shadow">
+                    <attr name="originalFile" stringvalue="Projects/Actions"/>
+                    <attr name="position" intvalue="2900"/>
+                </file>
+                <file name="sep-7.instance">
+                    <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
+                    <attr name="position" intvalue="3000"/>
+                </file>
+                <file name="org-netbeans-modules-project-ui-CustomizeProject.shadow">
+                    <attr name="originalFile" stringvalue="Actions/Project/org-netbeans-modules-project-ui-CustomizeProject.instance"/>
+                    <attr name="position" intvalue="3200"/>
+                </file>
+            </folder>
+        </folder>
+    </folder>
+
+    <folder name="Templates">
+        <folder name="meson">
+            <attr name="position" intvalue="1350"/>
+            <attr name="displayName" bundlevalue="org.netbeans.modules.cnd.meson.editor.Bundle#Templates_meson" />
+        </folder>
+    </folder>
+</filesystem>
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/meson.build.template b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/meson.build.template
new file mode 100644
index 0000000..a60777c
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/meson.build.template
@@ -0,0 +1,23 @@
+<#--
+
+    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.
+
+-->
+<#assign licensePrefix = "# ">
+<#include "${project.licensePath}">
+
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/meson.options.template b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/meson.options.template
new file mode 100644
index 0000000..6327eff
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/meson.options.template
@@ -0,0 +1,24 @@
+<#--
+
+    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.
+
+-->
+<#assign licensePrefix = "# ">
+<#include "${project.licensePath}">
+
+option('', type: '')
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/mime_resolver.xml b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/mime_resolver.xml
new file mode 100644
index 0000000..6c7af27
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/mime_resolver.xml
@@ -0,0 +1,41 @@
+<?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.
+
+-->
+<!DOCTYPE MIME-Resolver PUBLIC "-//NetBeans//DTD MIME Resolver 1.0//EN"
+		    "http://www.netbeans.org/dtds/mime-resolver-1_0.dtd">
+
+<MIME-resolver>
+    <file>
+        <name name="meson.build" substring="false" ignorecase="false"/>
+        <ext name="build"/>
+        <resolver mime="text/x-meson-build"/>
+    </file>
+    <file>
+        <name name="meson.options" substring="false" ignorecase="false"/>
+        <ext name="options"/>
+        <resolver mime="text/x-meson-options"/>
+    </file>
+    <file>
+        <name name="meson_options.txt" substring="false" ignorecase="false"/>
+        <ext name="txt"/>
+        <resolver mime="text/x-meson-options"/>
+    </file>
+</MIME-resolver>
\ No newline at end of file
diff --git a/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/project_icon.png b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/project_icon.png
new file mode 100644
index 0000000..37a0ea4
--- /dev/null
+++ b/cnd/cnd.meson/src/org/netbeans/modules/cnd/meson/resources/project_icon.png
Binary files differ
diff --git a/cnd/cnd.ui/nbproject/project.xml b/cnd/cnd.ui/nbproject/project.xml
index 8113d4f..11dc124 100644
--- a/cnd/cnd.ui/nbproject/project.xml
+++ b/cnd/cnd.ui/nbproject/project.xml
@@ -403,6 +403,7 @@
                 <friend>org.netbeans.modules.cnd.make2netbeans</friend>
                 <friend>org.netbeans.modules.cnd.makeproject</friend>
                 <friend>org.netbeans.modules.cnd.makeproject.ui</friend>
+                <friend>org.netbeans.modules.cnd.meson</friend>
                 <friend>org.netbeans.modules.cnd.modeldiscovery</friend>
                 <friend>org.netbeans.modules.cnd.modelimpl</friend>
                 <friend>org.netbeans.modules.cnd.modelutil</friend>
diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties
index efc235e..5911caf 100644
--- a/nbbuild/cluster.properties
+++ b/nbbuild/cluster.properties
@@ -1036,6 +1036,7 @@
         cnd.makeproject,\
         cnd.makeproject.source.bridge,\
         cnd.makeproject.ui,\
+        cnd.meson,\
         cnd.remote,\
         cnd.remote.projectui,\
         cnd.remote.ui,\