diff --git a/changelog/dpath.dd b/changelog/dpath.dd new file mode 100644 index 0000000..098bf00 --- /dev/null +++ b/changelog/dpath.dd @@ -0,0 +1,34 @@ +DUB settings & packages directory placement overhauled + +You can now configure where DUB places its downloaded packages and where the user configuration is stored through environment variables or through the dub configuration. You need to use an environment variable or the system-wide dub configuration to specify where the user configuration is stored. + +By default DUB stores the packages on +- Windows: `%APPDATA%/dub/settings.json` + `%LOCALAPPDATA%/dub/packages/` +- Posix: `$HOME/.dub/{packages/,settings.json}` + +now if the `DUB_HOME` environment variable is set it instead stores the packages (and other config) in +- `$DUB_HOME/{packages/,settings.json}` + +alternatively if `DUB_HOME` isn't set, but `DPATH` is set, the following path is used: +- `$DPATH/dub/{packages/,settings.json}` + +The `DPATH` environment variable is intended to be used by all D tooling related things doing user-space installation of things. It can be used to avoid cluttering the home folder. + +Additionally to environment variables it is possible to configure the package placement path + settings.json path through DUB's settings.json file. To configure where the user-editable settings.json is placed you need to adjust the system-wide dub configuration. + +In the settings.json you can set the following fields: + +```json +{ + "dubHome": "/path/to/dub", // sets both package store and config location +} +``` + +Additionally, these config paths will have environment variables using the `$VARIABLE` syntax resolved. + +The following list describes which path is going to be picked, from top to bottom, stopping whenever one is found: + +- `$DUB_HOME` environment variable +- `$DPATH` environment variable +- system-wide settings.json: `"dubHome"` property (only for userSettings) +- most specific settings.json: `"dubHome"` property (only for localRepository) diff --git a/source/dub/dub.d b/source/dub/dub.d index 372fa9a..b62fffe 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -194,6 +194,13 @@ private void init(NativePath root_path) { + loadConfigAndSetDirs(root_path); + + determineDefaultCompiler(); + } + + private void loadConfigAndSetDirs(NativePath root_path) + { import configy.Read; this.m_dirs = SpecialDirs.make(); @@ -213,17 +220,54 @@ const dubFolderPath = NativePath(thisExePath).parentPath; + // override default userSettings + localRepository if a $DPATH or + // $DUB_HOME environment variable is set. + bool overrideDubHomeFromEnv; + { + string dubHome = environment.get("DUB_HOME"); + if (!dubHome.length) { + auto dpath = environment.get("DPATH"); + if (dpath.length) + dubHome = (NativePath(dpath) ~ "dub/").toNativeString(); + + } + if (dubHome.length) { + overrideDubHomeFromEnv = true; + + m_dirs.userSettings = NativePath(dubHome); + m_dirs.localRepository = m_dirs.userSettings; + } + } + readSettingsFile(m_dirs.systemSettings ~ "settings.json"); readSettingsFile(dubFolderPath ~ "../etc/dub/settings.json"); version (Posix) { if (dubFolderPath.absolute && dubFolderPath.startsWith(NativePath("usr"))) readSettingsFile(NativePath("/etc/dub/settings.json")); } + + // Override user + local package path from system / binary settings + // Then continues loading local settings from these folders. (keeping + // global /etc/dub/settings.json settings intact) + // + // Don't use it if either $DPATH or $DUB_HOME are set, as environment + // variables usually take precedence over configuration. + if (!overrideDubHomeFromEnv && this.m_config.dubHome.set) { + m_dirs.userSettings = NativePath(this.m_config.dubHome.expandEnvironmentVariables); + } + + // load user config: readSettingsFile(m_dirs.userSettings ~ "settings.json"); + + // load per-package config: if (!root_path.empty) readSettingsFile(root_path ~ "dub.settings.json"); - determineDefaultCompiler(); + // same as userSettings above, but taking into account the + // config loaded from user settings and per-package config as well. + if (!overrideDubHomeFromEnv && this.m_config.dubHome.set) { + m_dirs.localRepository = NativePath(this.m_config.dubHome.expandEnvironmentVariables); + } } unittest @@ -304,7 +348,7 @@ assert(dub.computePkgSuppliers(null, SkipPackageSuppliers.standard, null).length == 0); assert(dub.computePkgSuppliers(null, SkipPackageSuppliers.standard, "http://example.com/") - .length == 1); + .length == 1); } @property bool dryRun() const { return m_dryRun; } @@ -1828,6 +1872,7 @@ SetInfo!(string[string]) defaultPostBuildEnvironments; SetInfo!(string[string]) defaultPreRunEnvironments; SetInfo!(string[string]) defaultPostRunEnvironments; + SetInfo!(string) dubHome; /// Merge a lower priority config (`this`) with a `higher` priority config public UserConfiguration merge(UserConfiguration higher) diff --git a/source/dub/project.d b/source/dub/project.d index 79da73a..1661f8a 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -1518,6 +1518,25 @@ assert(expandVars!expandVar("$${DUB_EXE:-dub}") == "${DUB_EXE:-dub}"); } +/// Expands the variables in the input string with the same rules as command +/// variables inside custom dub commands. +/// +/// Params: +/// s = the input string where environment variables in form `$VAR` should be replaced +/// throwIfMissing = if true, throw an exception if the given variable is not found, +/// otherwise replace unknown variables with the empty string. +string expandEnvironmentVariables(string s, bool throwIfMissing = true) +{ + import std.process : environment; + + return expandVars!((v) { + auto ret = environment.get(v); + if (ret is null && throwIfMissing) + throw new Exception("Specified environment variable `$" ~ v ~ "` is not set"); + return ret; + })(s); +} + // Keep the following list up-to-date if adding more build settings variables. /// List of variables that can be used in build settings package(dub) immutable buildSettingsVars = [ diff --git a/test/dpath-variable.sh b/test/dpath-variable.sh new file mode 100755 index 0000000..7e68020 --- /dev/null +++ b/test/dpath-variable.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +. $(dirname "${BASH_SOURCE[0]}")/common.sh +export DPATH="${CURR_DIR}/dpath-variable/dpath" +rm -rf "$DPATH" +cd "${CURR_DIR}/dpath-variable" +"${DUB}" upgrade + +if [[ ! -f "$DPATH/dub/packages/gitcompatibledubpackage-1.0.1/gitcompatibledubpackage/dub.json" ]]; then + die $LINENO 'Did not get dependencies installed into $DPATH.' +fi + +# just for making this shell script easier to write, copy the variable +DPATH_ALIAS="$DPATH" +# unset the variable so DUB doesn't pick it up though +unset DPATH +rm -rf "$DPATH_ALIAS" +echo '{"dubHome":"'"$DPATH_ALIAS"/dub2'"}' > "${CURR_DIR}/dpath-variable/dub.settings.json" + +function cleanup { + rm "${CURR_DIR}/dpath-variable/dub.settings.json" +} +trap cleanup EXIT + +"${DUB}" upgrade + +if [[ ! -f "$DPATH_ALIAS/dub2/packages/gitcompatibledubpackage-1.0.1/gitcompatibledubpackage/dub.json" ]]; then + die $LINENO 'Did not get dependencies installed into dubHome (set from config).' +fi diff --git a/test/dpath-variable/.gitignore b/test/dpath-variable/.gitignore new file mode 100644 index 0000000..9cfa21e --- /dev/null +++ b/test/dpath-variable/.gitignore @@ -0,0 +1 @@ +dpath diff --git a/test/dpath-variable/dub.json b/test/dpath-variable/dub.json new file mode 100644 index 0000000..0d25e30 --- /dev/null +++ b/test/dpath-variable/dub.json @@ -0,0 +1,6 @@ +{ + "name": "dpath-variable", + "dependencies": { + "gitcompatibledubpackage": "1.0.1" + } +} diff --git a/test/dpath-variable/source/app.d b/test/dpath-variable/source/app.d new file mode 100644 index 0000000..9198103 --- /dev/null +++ b/test/dpath-variable/source/app.d @@ -0,0 +1,3 @@ +void main() +{ +}