8223678: Add Visual Studio Code workspace generation support (for native code)
authorrwestberg
Mon, 03 Jun 2019 10:28:03 +0200
changeset 55166 2ae056696b15
parent 55165 a82a367b2d8c
child 55167 b003077e495c
8223678: Add Visual Studio Code workspace generation support (for native code) Reviewed-by: erikj
doc/ide.html
doc/ide.md
make/Main.gmk
make/UpdateBuildDocs.gmk
make/common/MakeBase.gmk
make/common/Utils.gmk
make/vscode/CreateVSCodeProject.gmk
make/vscode/indexers/ccls-extensions.txt
make/vscode/indexers/ccls-notes.txt
make/vscode/indexers/ccls-settings.txt
make/vscode/indexers/clangd-extensions.txt
make/vscode/indexers/clangd-notes.txt
make/vscode/indexers/clangd-settings.txt
make/vscode/indexers/cpptools-extensions.txt
make/vscode/indexers/cpptools-settings.txt
make/vscode/indexers/rtags-extensions.txt
make/vscode/indexers/rtags-settings.txt
make/vscode/template-launch.jsonc
make/vscode/template-tasks.jsonc
make/vscode/template-workspace-folder.txt
make/vscode/template-workspace.jsonc
test/make/TestMakeBase.gmk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/ide.html	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
+<head>
+  <meta charset="utf-8" />
+  <meta name="generator" content="pandoc" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
+  <title>IDE support in the JDK</title>
+  <style type="text/css">
+      code{white-space: pre-wrap;}
+      span.smallcaps{font-variant: small-caps;}
+      span.underline{text-decoration: underline;}
+      div.column{display: inline-block; vertical-align: top; width: 50%;}
+  </style>
+  <link rel="stylesheet" href="../make/data/docs-resources/resources/jdk-default.css" />
+  <!--[if lt IE 9]>
+    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
+  <![endif]-->
+</head>
+<body>
+<header id="title-block-header">
+<h1 class="title">IDE support in the JDK</h1>
+</header>
+<nav id="TOC">
+<ul>
+<li><a href="#introduction">Introduction</a><ul>
+<li><a href="#ide-support-for-native-code">IDE support for native code</a></li>
+<li><a href="#ide-support-for-java-code">IDE support for Java code</a></li>
+</ul></li>
+</ul>
+</nav>
+<h2 id="introduction">Introduction</h2>
+<p>When you are familiar with building and testing the JDK, you may want to configure an IDE to work with the source code. The instructions differ a bit depending on whether you are interested in working with the native (C/C++) or the Java code.</p>
+<h3 id="ide-support-for-native-code">IDE support for native code</h3>
+<p>There are a few ways to generate IDE configuration for the native sources, depending on which IDE to use.</p>
+<h4 id="visual-studio-code">Visual Studio Code</h4>
+<p>The make system can generate a <a href="https://code.visualstudio.com">Visual Studio Code</a> workspace that has C/C++ source indexing configured correctly, as well as launcher targets for tests and the Java launcher. After configuring, a workspace for the configuration can be generated using:</p>
+<pre class="shell"><code>make vscode-project</code></pre>
+<p>This creates a file called <code>jdk.code-workspace</code> in the build output folder. The full location will be printed after the workspace has been generated. To use it, choose <code>File -&gt; Open Workspace...</code> in Visual Studio Code.</p>
+<h5 id="alternative-indexers">Alternative indexers</h5>
+<p>The main <code>vscode-project</code> target configures the default C++ support in Visual Studio Code. There are also other source indexers that can be installed, that may provide additional features. It's currently possible to generate configuration for two such indexers, <a href="https://clang.llvm.org/extra/clangd/">clangd</a> and <a href="https://github.com/Andersbakken/rtags">rtags</a>. These can be configured by appending the name of the indexer to the make target, such as:</p>
+<pre class="shell"><code>make vscode-project-clangd</code></pre>
+<p>Additional instructions for configuring the given indexer will be displayed after the workspace has been generated.</p>
+<h4 id="visual-studio">Visual Studio</h4>
+<p>This section is a work in progress.</p>
+<pre class="shell"><code>make ide-project</code></pre>
+<h4 id="compilation-database">Compilation Database</h4>
+<p>The make system can generate generic native code indexing support in the form of a <a href="https://clang.llvm.org/docs/JSONCompilationDatabase.html">Compilation Database</a> that can be used by many different IDEs and source code indexers.</p>
+<pre class="shell"><code>make compile-commands</code></pre>
+<p>It's also possible to generate the Compilation Database for the HotSpot source code only, which is a bit faster as it includes less information.</p>
+<pre class="shell"><code>make compile-commands-hotspot</code></pre>
+<h3 id="ide-support-for-java-code">IDE support for Java code</h3>
+<p>This section is a work in progress.</p>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/ide.md	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,73 @@
+% IDE support in the JDK
+
+## Introduction
+
+When you are familiar with building and testing the JDK, you may want to
+configure an IDE to work with the source code. The instructions differ a bit
+depending on whether you are interested in working with the native (C/C++) or
+the Java code.
+
+### IDE support for native code
+
+There are a few ways to generate IDE configuration for the native sources,
+depending on which IDE to use.
+
+#### Visual Studio Code
+
+The make system can generate a [Visual Studio Code](https://code.visualstudio.com)
+workspace that has C/C++ source indexing configured correctly, as well as
+launcher targets for tests and the Java launcher. After configuring, a workspace
+for the configuration can be generated using:
+
+```shell
+make vscode-project
+```
+
+This creates a file called `jdk.code-workspace` in the build output folder. The
+full location will be printed after the workspace has been generated. To use it,
+choose `File -> Open Workspace...` in Visual Studio Code.
+
+##### Alternative indexers
+
+The main `vscode-project` target configures the default C++ support in Visual
+Studio Code. There are also other source indexers that can be installed, that
+may provide additional features. It's currently possible to generate
+configuration for two such indexers, [clangd](https://clang.llvm.org/extra/clangd/)
+and [rtags](https://github.com/Andersbakken/rtags). These can be configured by
+appending the name of the indexer to the make target, such as:
+
+```shell
+make vscode-project-clangd
+```
+
+Additional instructions for configuring the given indexer will be displayed
+after the workspace has been generated.
+
+#### Visual Studio
+
+This section is a work in progress.
+
+```shell
+make ide-project
+```
+
+#### Compilation Database
+
+The make system can generate generic native code indexing support in the form of
+a [Compilation Database](https://clang.llvm.org/docs/JSONCompilationDatabase.html)
+that can be used by many different IDEs and source code indexers.
+
+```shell
+make compile-commands
+```
+
+It's also possible to generate the Compilation Database for the HotSpot source
+code only, which is a bit faster as it includes less information.
+
+```shell
+make compile-commands-hotspot
+```
+
+### IDE support for Java code
+
+This section is a work in progress.
\ No newline at end of file
--- a/make/Main.gmk	Mon Jun 03 17:14:23 2019 -0700
+++ b/make/Main.gmk	Mon Jun 03 10:28:03 2019 +0200
@@ -288,6 +288,27 @@
 ALL_TARGETS += compile-commands compile-commands-hotspot
 
 ################################################################################
+# VS Code projects
+vscode-project:
+	+($(CD) $(TOPDIR)/make/vscode && $(MAKE) $(MAKE_ARGS) -f CreateVSCodeProject.gmk \
+      VSCODE_INDEXER=cpptools)
+
+vscode-project-clangd:
+	+($(CD) $(TOPDIR)/make/vscode && $(MAKE) $(MAKE_ARGS) -f CreateVSCodeProject.gmk \
+      VSCODE_INDEXER=clangd)
+
+vscode-project-rtags:
+	+($(CD) $(TOPDIR)/make/vscode && $(MAKE) $(MAKE_ARGS) -f CreateVSCodeProject.gmk \
+      VSCODE_INDEXER=rtags)
+
+vscode-project-ccls:
+	+($(CD) $(TOPDIR)/make/vscode && $(MAKE) $(MAKE_ARGS) -f CreateVSCodeProject.gmk \
+      VSCODE_INDEXER=ccls)
+
+ALL_TARGETS += vscode-project vscode-project-clangd vscode-project-rtags \
+  vscode-project-ccls
+
+################################################################################
 # Build demos targets
 
 demos-jdk:
@@ -774,6 +795,11 @@
   compile-commands-hotspot: $(COMPILE_COMMANDS_TARGETS_HOTSPOT)
   compile-commands: $(COMPILE_COMMANDS_TARGETS_HOTSPOT) $(COMPILE_COMMANDS_TARGETS_JDK)
 
+  vscode-project: compile-commands
+  vscode-project-clangd: compile-commands
+  vscode-project-rtags: compile-commands
+  vscode-project-ccls: compile-commands
+
   # Jmods cannot be created until we have the jmod tool ready to run. During
   # a normal build we run it from the exploded image, but when cross compiling
   # it's run from the buildjdk, which is either created at build time or user
--- a/make/UpdateBuildDocs.gmk	Mon Jun 03 17:14:23 2019 -0700
+++ b/make/UpdateBuildDocs.gmk	Mon Jun 03 10:28:03 2019 +0200
@@ -61,6 +61,14 @@
 ))
 TARGETS += $(testing)
 
+$(eval $(call SetupProcessMarkdown, ide, \
+  FILES := $(DOCS_DIR)/ide.md, \
+  DEST := $(DOCS_DIR), \
+  CSS := $(GLOBAL_SPECS_DEFAULT_CSS_FILE), \
+  OPTIONS := --toc, \
+))
+TARGETS += $(ide)
+
 ################################################################################
 
 $(eval $(call IncludeCustomExtension, UpdateBuildDocs.gmk))
--- a/make/common/MakeBase.gmk	Mon Jun 03 17:14:23 2019 -0700
+++ b/make/common/MakeBase.gmk	Mon Jun 03 10:28:03 2019 +0200
@@ -472,6 +472,22 @@
 endif
 
 ################################################################################
+# FixPathList
+#
+# On Windows, converts a cygwin/unix style path list (colon-separated) into
+# the native format (mixed mode, semicolon-separated). On other platforms,
+# return the path list unchanged.
+################################################################################
+ifeq ($(call isTargetOs, windows), true)
+  FixPathList = \
+      $(subst @,$(SPACE),$(subst $(SPACE),;,$(foreach entry,$(subst :,$(SPACE),\
+      $(subst $(SPACE),@,$(strip $1))),$(call FixPath, $(entry)))))
+else
+  FixPathList = \
+      $1
+endif
+
+################################################################################
 # DependOnVariable
 #
 # This macro takes a variable name and puts the value in a file only if the
--- a/make/common/Utils.gmk	Mon Jun 03 17:14:23 2019 -0700
+++ b/make/common/Utils.gmk	Mon Jun 03 10:28:03 2019 +0200
@@ -122,7 +122,8 @@
 # $2 - Directory to compute the relative path from
 RelativePath = \
     $(eval $1_prefix := $(call FindCommonPathPrefix, $1, $2)) \
-    $(eval $1_dotdots := $(call DirToDotDot, $(patsubst $($(strip $1)_prefix)/%, %, $2))) \
+    $(eval $1_dotdots := $(call DirToDotDot, $(patsubst $($(strip $1)_prefix)%, %, $2))) \
+    $(eval $1_dotdots := $(if $($(strip $1)_dotdots),$($(strip $1)_dotdots),.)) \
     $(eval $1_suffix := $(patsubst $($(strip $1)_prefix)/%, %, $1)) \
     $($(strip $1)_dotdots)/$($(strip $1)_suffix)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/CreateVSCodeProject.gmk	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,113 @@
+#
+# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+# This must be the first rule
+default: all
+
+include $(SPEC)
+include MakeBase.gmk
+
+################################################################################
+# Return the full path to an indexer-specific file fragment.
+#
+# Param 1: Fragment name
+################################################################################
+GetIndexerFragment = \
+    $(TOPDIR)/make/vscode/indexers/$(VSCODE_INDEXER)-$(1).txt
+
+################################################################################
+# Show indexer-specific notes if they exist, otherwise do nothing
+################################################################################
+ifneq (,$(wildcard $(call GetIndexerFragment,notes)))
+  ShowIndexerNotes = $(CAT) $(call GetIndexerFragment,notes)
+else
+  ShowIndexerNotes =
+endif
+
+################################################################################
+# Return the platform-dependent preferred debug engine name.
+################################################################################
+ifeq ($(call isTargetOs, windows), true)
+  DebugEngineName = cppvsdbg
+else
+  DebugEngineName = cppdbg
+endif
+
+################################################################################
+# Return an additional configuration fragment if the WORKSPACE_ROOT is different
+# from TOPDIR.
+################################################################################
+ifneq ($(WORKSPACE_ROOT),$(TOPDIR))
+  GetExtraWorkspaceRoot = $(TOPDIR)/make/vscode/template-workspace-folder.txt
+else
+  GetExtraWorkspaceRoot = /dev/null
+endif
+
+################################################################################
+# Create a project configuration from a given template, replacing a known set
+# of variables.
+#
+# Param 1: Template
+# Param 2: Output
+################################################################################
+define CreateFromTemplate
+	$(call LogInfo, Generating $2)
+	$(call MakeDir, $(dir $2))
+	$(SED) -e '/{{INDEXER_EXTENSIONS}}/r $(call GetIndexerFragment,extensions)' \
+	    -e '/{{INDEXER_SETTINGS}}/r $(call GetIndexerFragment,settings)' \
+	    -e '/{{EXTRA_WORKSPACE_ROOT}}/r $(call GetExtraWorkspaceRoot)' $1 | \
+	$(SED) -e 's!{{TOPDIR}}!$(call FixPath,$(TOPDIR))!g' \
+	    -e 's!{{TOPDIR_RELATIVE}}!$(call FixPath,$(strip \
+	        $(call RelativePath,$(OUTPUTDIR),$(TOPDIR))))!g' \
+	    -e 's!{{WORKSPACE_ROOT}}!$(call FixPath,$(WORKSPACE_ROOT))!g' \
+	    -e 's!{{OUTPUTDIR}}!$(call FixPath,$(OUTPUTDIR))!g' \
+	    -e 's!{{CONF_NAME}}!$(CONF_NAME)!g' \
+	    -e 's!{{COMPILER}}!$(call FixPath,$(CXX)) $(SYSROOT_CFLAGS)!g' \
+	    -e 's!{{MAKE}}!$(call FixPath,$(MAKE))!g' \
+	    -e 's!{{PATH}}!$(call FixPathList,$(PATH))!g' \
+	    -e 's!{{DEBUGENGINENAME}}!$(call DebugEngineName)!g' \
+	    -e '/{{INDEXER_EXTENSIONS}}/d' \
+	    -e '/{{INDEXER_SETTINGS}}/d' \
+	    -e '/{{EXTRA_WORKSPACE_ROOT}}/d' \
+	    > $2
+endef
+
+$(OUTPUTDIR)/jdk.code-workspace:
+	$(call LogWarn, Creating workspace $@)
+	$(call CreateFromTemplate, $(TOPDIR)/make/vscode/template-workspace.jsonc, $@)
+	$(call ShowIndexerNotes)
+
+$(OUTPUTDIR)/.vscode/tasks.json:
+	$(call CreateFromTemplate, $(TOPDIR)/make/vscode/template-tasks.jsonc, $@)
+
+$(OUTPUTDIR)/.vscode/launch.json:
+	$(call CreateFromTemplate, $(TOPDIR)/make/vscode/template-launch.jsonc, $@)
+
+TARGETS := $(OUTPUTDIR)/jdk.code-workspace $(OUTPUTDIR)/.vscode/tasks.json \
+    $(OUTPUTDIR)/.vscode/launch.json
+
+all: $(TARGETS)
+
+.PHONY: all $(TARGETS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/indexers/ccls-extensions.txt	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,2 @@
+			"ms-vscode.cpptools",
+			"ccls-project.ccls"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/indexers/ccls-notes.txt	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,3 @@
+
+* The "ccls" indexer must be present in PATH, or configured with "ccls.launch.command" in user preferences.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/indexers/ccls-settings.txt	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,28 @@
+		// Configure cpptools IntelliSense
+		"C_Cpp.intelliSenseCachePath": "{{OUTPUTDIR}}/.vscode",
+		"C_Cpp.default.compileCommands": "{{OUTPUTDIR}}/compile_commands.json",
+		"C_Cpp.default.cppStandard": "c++03",
+		"C_Cpp.default.compilerPath": "{{COMPILER}}",
+
+		// Configure ccls
+		"ccls.misc.compilationDatabaseDirectory": "{{TOPDIR_RELATIVE}}",
+		"ccls.cache.hierarchicalPath": true,
+		"ccls.cache.directory": "{{OUTPUTDIR}}/.vscode/ccls",
+
+		// Avoid issues with precompiled headers
+		"ccls.clang.excludeArgs": [
+			// Windows / MSVC
+			"-Fp{{OUTPUTDIR}}/hotspot/variant-server/libjvm/objs/BUILD_LIBJVM.pch",
+			"-Fp{{OUTPUTDIR}}/hotspot/variant-server/libjvm/gtest/objs/BUILD_GTEST_LIBJVM.pch",
+			"-Yuprecompiled.hpp",
+			// MacOS / clang
+			"{{OUTPUTDIR}}/hotspot/variant-server/libjvm/objs/precompiled/precompiled.hpp.pch",
+			"{{OUTPUTDIR}}/hotspot/variant-server/libjvm/gtest/objs/precompiled/precompiled.hpp.pch",
+			"-include-pch"
+		],
+
+		// Disable conflicting features from cpptools
+		"C_Cpp.autocomplete": "Disabled",
+		"C_Cpp.errorSquiggles": "Disabled",
+		"C_Cpp.formatting": "Disabled",
+		"C_Cpp.intelliSenseEngine": "Disabled",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/indexers/clangd-extensions.txt	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,2 @@
+			"ms-vscode.cpptools",
+			"llvm-vs-code-extensions.vscode-clangd"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/indexers/clangd-notes.txt	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,4 @@
+
+* The "clangd" indexer must be present in PATH, or configured with "clangd.path" in user preferences.
+* If building with clang (default on OSX), precompiled headers must be disabled.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/indexers/clangd-settings.txt	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,17 @@
+		// Configure cpptools IntelliSense
+		"C_Cpp.intelliSenseCachePath": "{{OUTPUTDIR}}/.vscode",
+		"C_Cpp.default.compileCommands": "{{OUTPUTDIR}}/compile_commands.json",
+		"C_Cpp.default.cppStandard": "c++03",
+		"C_Cpp.default.compilerPath": "{{COMPILER}}",
+
+		// Configure clangd
+		"clangd.arguments": [
+			"-background-index",
+			"-compile-commands-dir={{OUTPUTDIR}}"
+		],
+
+		// Disable conflicting features from cpptools
+		"C_Cpp.autocomplete": "Disabled",
+		"C_Cpp.errorSquiggles": "Disabled",
+		"C_Cpp.formatting": "Disabled",
+		"C_Cpp.intelliSenseEngine": "Disabled",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/indexers/cpptools-extensions.txt	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,1 @@
+			"ms-vscode.cpptools"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/indexers/cpptools-settings.txt	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,5 @@
+		// Configure cpptools IntelliSense
+		"C_Cpp.intelliSenseCachePath": "{{OUTPUTDIR}}/.vscode",
+		"C_Cpp.default.compileCommands": "{{OUTPUTDIR}}/compile_commands.json",
+		"C_Cpp.default.cppStandard": "c++03",
+		"C_Cpp.default.compilerPath": "{{COMPILER}}",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/indexers/rtags-extensions.txt	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,2 @@
+			"ms-vscode.cpptools",
+			"jomiller.rtags-client"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/indexers/rtags-settings.txt	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,14 @@
+		// Configure cpptools IntelliSense
+		"C_Cpp.intelliSenseCachePath": "{{OUTPUTDIR}}/.vscode",
+		"C_Cpp.default.compileCommands": "{{OUTPUTDIR}}/compile_commands.json",
+		"C_Cpp.default.cppStandard": "c++03",
+		"C_Cpp.default.compilerPath": "{{COMPILER}}",
+
+		// Configure RTags
+		"rtags.misc.compilationDatabaseDirectory": "{{OUTPUTDIR}}",
+
+		// Disable conflicting features from cpptools
+		"C_Cpp.autocomplete": "Disabled",
+		"C_Cpp.errorSquiggles": "Disabled",
+		"C_Cpp.formatting": "Disabled",
+		"C_Cpp.intelliSenseEngine": "Disabled",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/template-launch.jsonc	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,55 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "gtestLauncher",
+            "type": "{{DEBUGENGINENAME}}",
+            "request": "launch",
+            "program": "{{OUTPUTDIR}}/hotspot/variant-server/libjvm/gtest/gtestLauncher",
+            "args": ["-jdk:{{OUTPUTDIR}}/jdk"],
+            "stopAtEntry": false,
+            "cwd": "{{WORKSPACE_ROOT}}",
+            "environment": [],
+            "externalConsole": false,
+            "preLaunchTask": "Make 'exploded-image'",
+            "osx": {
+                "MIMode": "lldb",
+                "internalConsoleOptions": "openOnSessionStart",
+                "args": ["--gtest_color=no", "-jdk:{{OUTPUTDIR}}/jdk"]
+            },
+            "linux": {
+                "MIMode": "gdb",
+                "setupCommands": [
+                    {
+                        "text": "handle SIGSEGV noprint nostop",
+                        "description": "Disable stopping on signals handled by the JVM"
+                    }
+                ]
+            }
+        },
+        {
+            "name": "java",
+            "type": "{{DEBUGENGINENAME}}",
+            "request": "launch",
+            "program": "{{OUTPUTDIR}}/jdk/bin/java",
+            "stopAtEntry": false,
+            "cwd": "{{WORKSPACE_ROOT}}",
+            "environment": [],
+            "externalConsole": false,
+            "preLaunchTask": "Make 'exploded-image'",
+            "osx": {
+                "MIMode": "lldb",
+                "internalConsoleOptions": "openOnSessionStart",
+            },
+            "linux": {
+                "MIMode": "gdb",
+                "setupCommands": [
+                    {
+                        "text": "handle SIGSEGV noprint nostop",
+                        "description": "Disable stopping on signals handled by the JVM"
+                    }
+                ]
+            }
+        }
+    ]
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/template-tasks.jsonc	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,55 @@
+{
+    // See https://go.microsoft.com/fwlink/?LinkId=733558
+    // for the documentation about the tasks.json format
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "Update compilation database (compile_commands.json)",
+            "type": "shell",
+            "options": {
+                "env": {
+                    "PATH": "{{PATH}}"
+                },
+                "cwd": "{{WORKSPACE_ROOT}}"
+            },
+            "command": "{{MAKE}} CONF_NAME={{CONF_NAME}} compile-commands",
+            "problemMatcher": []
+        },
+        {
+            "label": "Make 'hotspot'",
+            "type": "shell",
+            "options": {
+                "env": {
+                    "PATH": "{{PATH}}"
+                },
+                "cwd": "{{WORKSPACE_ROOT}}"
+            },
+            "command": "{{MAKE}} CONF_NAME={{CONF_NAME}} hotspot",
+            "problemMatcher": ["$gcc"]
+        },
+        {
+            "label": "Make 'exploded-image'",
+            "type": "shell",
+            "options": {
+                "env": {
+                    "PATH": "{{PATH}}"
+                },
+                "cwd": "{{WORKSPACE_ROOT}}"
+            },
+            "command": "{{MAKE}} CONF_NAME={{CONF_NAME}} exploded-image",
+            "problemMatcher": ["$gcc"]
+        },
+        {
+            "label": "Make 'jdk'",
+            "type": "shell",
+            "options": {
+                "env": {
+                    "PATH": "{{PATH}}"
+                },
+                "cwd": "{{WORKSPACE_ROOT}}"
+            },
+            "command": "{{MAKE}} CONF_NAME={{CONF_NAME}} jdk",
+            "problemMatcher": ["$gcc"]
+        }
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/template-workspace-folder.txt	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,4 @@
+		{
+			"name": "Additional sources",
+			"path": "{{WORKSPACE_ROOT}}"
+		},
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/vscode/template-workspace.jsonc	Mon Jun 03 10:28:03 2019 +0200
@@ -0,0 +1,63 @@
+{
+	"folders": [
+		{
+			"name": "Source root",
+			"path": "{{TOPDIR}}"
+		},
+		// {{EXTRA_WORKSPACE_ROOT}}
+		{
+			"name": "Build artifacts",
+			"path": "{{OUTPUTDIR}}"
+		}
+	],
+	"extensions": {
+		"recommendations": [
+			// {{INDEXER_EXTENSIONS}}
+		]
+	},
+	"settings": {
+		// {{INDEXER_SETTINGS}}
+
+		// Additional conventions
+		"files.associations": {
+			"*.gmk": "makefile"
+		},
+
+		// Having these enabled slow down task execution
+		"typescript.tsc.autoDetect": "off",
+		"gulp.autoDetect": "off",
+		"npm.autoDetect": "off",
+		"grunt.autoDetect": "off",
+		"jake.autoDetect": "off",
+
+		// Certain types of files are not relevant for the file browser
+		"files.exclude": {
+			"**/.git": true,
+			"**/.hg": true,
+			"**/.DS_Store": true,
+		},
+
+		// Files that may be interesting to browse manually, but avoided during searches
+		"search.exclude": {
+			"**/*.class": true,
+			"**/*.jsa": true,
+			"**/*.vardeps": true,
+			"**/*.o": true,
+			"**/*.obj": true,
+			"**/*.d": true,
+			"**/*.d.*": true,
+			"**/*_batch*": true,
+			"**/*.marker": true,
+			"**/compile-commands/": true,
+			"**/objs": true,
+			"**/launcher-objs": true,
+			"**/*.cmdline": true,
+			"**/*.log": true,
+			".vscode": true,
+			".clangd": true
+		},
+
+		// Trailing whitespace should never be used in this project
+		"files.trimTrailingWhitespace": true
+	}
+}
\ No newline at end of file
--- a/test/make/TestMakeBase.gmk	Mon Jun 03 17:14:23 2019 -0700
+++ b/test/make/TestMakeBase.gmk	Mon Jun 03 10:28:03 2019 +0200
@@ -361,6 +361,18 @@
     RelativePath, \
 )
 
+$(call AssertEquals, \
+    $(call RelativePath, /foo/bar/baz/banan/kung, /foo/bar/baz), \
+    ./banan/kung, \
+    RelativePath, \
+)
+
+$(call AssertEquals, \
+    $(call RelativePath, /foo/bar/baz/banan/kung, /foo/bar/baz/), \
+    ./banan/kung, \
+    RelativePath, \
+)
+
 ################################################################################
 # Test ParseKeywordVariable