Skip to content

Commit 41d8ba4

Browse files
authored
Merge pull request #139 from mbland/plugin-scope
Implement correct scope for executing `@go` commands in plugin modules
2 parents 4de3d6a + 18f686c commit 41d8ba4

File tree

10 files changed

+300
-192
lines changed

10 files changed

+300
-192
lines changed

go-core.bash

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,6 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH"
248248
@go.search_plugins() {
249249
local __gsp_plugins_dir="$_GO_SCRIPTS_DIR/plugins"
250250

251-
# Set `_GO_PLUGINS_DIR` if called from a top-level `./go` script before `@go`.
252-
_GO_PLUGINS_DIR="${_GO_PLUGINS_DIR:-$_GO_SCRIPTS_DIR/plugins}"
253-
254251
while true; do
255252
if "$1" "$__gsp_plugins_dir"; then
256253
return
@@ -412,3 +409,4 @@ elif [[ -z "$COLUMNS" ]]; then
412409
fi
413410
export COLUMNS="${COLUMNS:-80}"
414411
fi
412+
_GO_PLUGINS_DIR="$_GO_SCRIPTS_DIR/plugins"

lib/bats-main

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export _GO_COVERALLS_URL="${_GO_COVERALLS_URL}"
7777

7878
# Collect coverage on Travis. Doesn't seem to slow anything down substantially.
7979
if [[ -n "$_GO_COVERALLS_URL" && "$TRAVIS_OS_NAME" == 'linux' ]]; then
80-
declare _GO_COLLECT_BATS_COVERAGE='true'
80+
_GO_COLLECT_BATS_COVERAGE='true'
8181
fi
8282

8383
# Array of `./go glob` arguments to select Bats test files in `_GO_TEST_DIR`

lib/internal/path

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,6 @@
11
#! /bin/bash
22

3-
_@go.set_search_paths_add_plugin_paths() {
4-
local plugin_paths=("$1"/*/bin)
5-
if [[ "${plugin_paths[0]}" != "$1/*/bin" ]]; then
6-
_GO_PLUGINS_PATHS+=("${plugin_paths[@]}")
7-
fi
8-
return 1
9-
}
10-
11-
_@go.set_search_paths() {
12-
local plugin_path
13-
14-
if [[ -n "$_GO_INJECT_SEARCH_PATH" ]]; then
15-
_GO_SEARCH_PATHS+=("$_GO_INJECT_SEARCH_PATH")
16-
fi
17-
_GO_SEARCH_PATHS+=("$_GO_CORE_DIR/libexec" "$_GO_SCRIPTS_DIR")
18-
19-
# A plugin's own local plugin paths will appear before inherited ones. If
20-
# there is a version incompatibility issue with other installed plugins, this
21-
# allows a plugin's preferred version to take precedence.
22-
@go.search_plugins '_@go.set_search_paths_add_plugin_paths'
23-
24-
# Ensure a plugin's _GO_SCRIPTS_DIR isn't duplicated in _GO_SEARCH_PATHS.
25-
for plugin_path in "${_GO_PLUGINS_PATHS[@]}"; do
26-
if [[ "$plugin_path" != "$_GO_SCRIPTS_DIR" ]]; then
27-
_GO_SEARCH_PATHS+=("$plugin_path")
28-
fi
29-
done
30-
}
31-
32-
if [[ "${#_GO_SEARCH_PATHS[@]}" -eq 0 ]]; then
33-
_GO_PLUGINS_DIR="$_GO_SCRIPTS_DIR/plugins"
34-
_@go.set_search_paths
35-
fi
3+
. "$_GO_CORE_DIR/lib/internal/set-search-paths"
364

375
_@go.list_available_commands() {
386
. "$_GO_CORE_DIR/lib/internal/commands"

lib/internal/set-search-paths

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#! /bin/bash
2+
3+
_@go.set_search_paths_add_plugin_paths() {
4+
local plugin_paths=("$1"/*/bin)
5+
local plugin_path
6+
7+
if [[ "${plugin_paths[0]}" != "$1/*/bin" ]]; then
8+
# Ensure a plugin's _GO_SCRIPTS_DIR isn't duplicated in _GO_PLUGINS_PATHS.
9+
for plugin_path in "${plugin_paths[@]}"; do
10+
if [[ "$plugin_path" != "$_GO_SCRIPTS_DIR" ]]; then
11+
_GO_PLUGINS_PATHS+=("$plugin_path")
12+
fi
13+
done
14+
fi
15+
return 1
16+
}
17+
18+
_@go.set_search_paths() {
19+
local plugin_path
20+
21+
if [[ -n "$_GO_INJECT_SEARCH_PATH" ]]; then
22+
_GO_SEARCH_PATHS+=("$_GO_INJECT_SEARCH_PATH")
23+
fi
24+
_GO_SEARCH_PATHS+=("$_GO_CORE_DIR/libexec" "$_GO_SCRIPTS_DIR")
25+
26+
# A plugin's own local plugin paths will appear before inherited ones. If
27+
# there is a version incompatibility issue with other installed plugins, this
28+
# allows a plugin's preferred version to take precedence.
29+
@go.search_plugins '_@go.set_search_paths_add_plugin_paths'
30+
_GO_SEARCH_PATHS+=("${_GO_PLUGINS_PATHS[@]}")
31+
}
32+
33+
if [[ "${#_GO_SEARCH_PATHS[@]}" -eq 0 ]]; then
34+
_@go.set_search_paths
35+
fi

lib/internal/use

Lines changed: 80 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -73,115 +73,106 @@
7373
# for modules imported within a Bash command script or function:
7474
# @go modules --imported
7575

76-
declare __go_module_name
77-
declare __go_module_file
78-
declare __go_loaded_module
79-
declare __go_loaded_file
80-
declare __go_module_index
81-
declare __go_use_caller
82-
declare __go_use_prev_caller
83-
84-
# Wrapper function necessary due to BASH_SOURCE and FUNCNAME bug in Bash <4.3.
85-
_@go.set_use_caller() {
86-
__go_use_caller="${BASH_SOURCE[2]}:${BASH_LINENO[1]} ${FUNCNAME[2]}"
87-
}
88-
8976
_@go.find_plugin_module() {
9077
[[ -f "$1/$__go_module_file" ]] && __go_module_file="$1/$__go_module_file"
9178
}
9279

9380
_@go.find_module() {
94-
# If a script imports a plugin module, and that module (`__go_use_caller`)
95-
# tries to import another module from the same plugin, this block will adjust
96-
# the search parameters accordingly.
97-
if [[ -n "$_GO_PLUGINS_DIR" &&
98-
"$__go_use_caller" =~ ^$_GO_PLUGINS_DIR/.*/lib/ ]]; then
99-
local _GO_SCRIPTS_DIR="${__go_use_caller%/lib/*}/bin"
100-
local _GO_ROOTDIR="${_GO_SCRIPTS_DIR%/bin}"
81+
if [[ -n "$_GO_INJECT_MODULE_PATH" ]]; then
82+
__go_module_file="$_GO_INJECT_MODULE_PATH/$__go_module_name"
83+
if [[ -f "$__go_module_file" ]]; then
84+
return
85+
fi
10186
fi
102-
__go_module_file="$_GO_SCRIPTS_DIR/lib/$__go_module_name"
87+
__go_module_file="$_GO_CORE_DIR/lib/$__go_module_name"
10388

10489
if [[ ! -f "$__go_module_file" ]]; then
105-
__go_module_file="$_GO_ROOTDIR/lib/$__go_module_name"
90+
__go_module_file="$_GO_SCRIPTS_DIR/lib/$__go_module_name"
10691

10792
if [[ ! -f "$__go_module_file" ]]; then
108-
# Convert <plugin>/<module> to plugins/<plugin>/lib/<module>
109-
__go_module_file="${__go_module_name/\///lib/}"
110-
@go.search_plugins '_@go.find_plugin_module'
93+
__go_module_file="$_GO_ROOTDIR/lib/$__go_module_name"
94+
95+
if [[ ! -f "$__go_module_file" ]]; then
96+
# Convert <plugin>/<module> to plugins/<plugin>/lib/<module>
97+
__go_module_file="${__go_module_name/\///lib/}"
98+
@go.search_plugins '_@go.find_plugin_module'
99+
fi
111100
fi
112101
fi
113102
}
114103

115-
for __go_module_name in "$@"; do
116-
# Since every import is in the same scope, setting the caller each time is
117-
# necessary in case any imported modules import other modules.
118-
_@go.set_use_caller
119-
__go_module_file="$_GO_CORE_DIR/lib/$__go_module_name"
120-
121-
if [[ -n "$_GO_INJECT_MODULE_PATH" ]]; then
122-
__go_module_file="$_GO_INJECT_MODULE_PATH/$__go_module_name"
123-
if [[ ! -f "$__go_module_file" ]]; then
124-
__go_module_file="$_GO_CORE_DIR/lib/$__go_module_name"
125-
fi
104+
_@go.import_module() {
105+
if [[ "${__go_module_file#$_GO_SCRIPTS_DIR}" =~ /plugins/[^/]+/lib/ ]]; then
106+
local _GO_SCRIPTS_DIR="${__go_module_file%/lib/*}/bin"
107+
local _GO_ROOTDIR="${_GO_SCRIPTS_DIR%/bin}"
108+
local _GO_PLUGINS_PATHS=()
109+
local _GO_SEARCH_PATHS=()
126110
fi
127111

128-
if [[ ! -f "$__go_module_file" ]] && ! _@go.find_module; then
129-
@go.printf 'ERROR: Module %s not found at:\n' "$__go_module_name" >&2
130-
@go.print_stack_trace 1 >&2
131-
exit 1
132-
fi
112+
# Prevent self- and circular importing by registering info before sourcing.
113+
_GO_IMPORTED_MODULES+=("$__go_module_name")
114+
_GO_IMPORTED_MODULE_FILES+=("$__go_module_file")
115+
_GO_IMPORTED_MODULE_CALLERS+=("$current_caller")
116+
. "$__go_module_file"
117+
}
133118

134-
# If we found the module in our project, but we're installed as a plugin,
135-
# change the loaded module name to reflect that.
136-
#
137-
# Using `##` to trim the string instead of `#` flattens the module name
138-
# namespace. We could use `#` to keep it hierarchical, but since the Bash
139-
# namespace itself is flat, this might lead to hard-to-debug collisions if
140-
# functions and variables get redefined. Keeping a flat module name namespace
141-
# allows us to detect such potential collisions and issue a warning below.
142-
if [[ -n "$_GO_PLUGINS_DIR" &&
143-
"$__go_module_file" =~ ^$_GO_PLUGINS_DIR ]]; then
144-
__go_module_name="${__go_module_file##*/plugins/}"
145-
__go_module_name="${__go_module_name/\/bin\///}"
146-
__go_module_name="${__go_module_name/\/lib\///}"
147-
fi
119+
_@go.use_modules() {
120+
local __go_module_name
121+
local __go_module_file
122+
local loaded_module
123+
local loaded_file
124+
local module_index
125+
local current_caller="${BASH_SOURCE[2]}:${BASH_LINENO[1]} ${FUNCNAME[2]}"
126+
local prev_caller
148127

149-
__go_module_index=0
150-
for __go_loaded_module in "${_GO_IMPORTED_MODULES[@]}"; do
151-
if [[ "$__go_module_name" == "$__go_loaded_module" ]]; then
152-
__go_loaded_file="${_GO_IMPORTED_MODULE_FILES[$__go_module_index]}"
153-
__go_use_prev_caller="${_GO_IMPORTED_MODULE_CALLERS[$__go_module_index]}"
128+
for __go_module_name in "$@"; do
129+
if ! _@go.find_module; then
130+
@go.printf 'ERROR: Module %s not found at:\n' "$__go_module_name" >&2
131+
@go.print_stack_trace 1 >&2
132+
exit 1
133+
fi
154134

155-
# This may happen if a plugin appears more than once in a project tree.
156-
if [[ "$__go_module_file" != "$__go_loaded_file" ]]; then
157-
@go.printf '%s\n' "WARNING: Module: $__go_module_name" \
158-
"imported at: $__go_use_caller" \
159-
"from file: $__go_module_file" \
160-
"previously imported at: $__go_use_prev_caller" \
161-
"from file: $__go_loaded_file" >&2
162-
fi
163-
continue 2
135+
# If we found the module in our project, but we're installed as a plugin,
136+
# change the loaded module name to reflect that.
137+
#
138+
# Using `##` to trim the string instead of `#` flattens the module name
139+
# namespace. We could use `#` to keep it hierarchical, but since the Bash
140+
# namespace itself is flat, this might lead to hard-to-debug collisions if
141+
# functions and variables get redefined. Keeping a flat module name
142+
# namespace allows us to detect such potential collisions and issue a
143+
# warning below.
144+
if [[ "$__go_module_file" =~ ^$_GO_PLUGINS_DIR/ ]]; then
145+
__go_module_name="${__go_module_file##*/plugins/}"
146+
__go_module_name="${__go_module_name/\/bin\///}"
147+
__go_module_name="${__go_module_name/\/lib\///}"
164148
fi
165-
((++__go_module_index))
166-
done
167149

168-
# Prevent self- and circular importing by registering info before sourcing.
169-
_GO_IMPORTED_MODULES+=("$__go_module_name")
170-
_GO_IMPORTED_MODULE_FILES+=("$__go_module_file")
171-
_GO_IMPORTED_MODULE_CALLERS+=("$__go_use_caller")
150+
module_index=0
151+
for loaded_module in "${_GO_IMPORTED_MODULES[@]}"; do
152+
if [[ "$__go_module_name" == "$loaded_module" ]]; then
153+
loaded_file="${_GO_IMPORTED_MODULE_FILES[$module_index]}"
154+
prev_caller="${_GO_IMPORTED_MODULE_CALLERS[$module_index]}"
172155

173-
if ! . "$__go_module_file"; then
174-
@go.printf 'ERROR: Failed to import %s module from %s at:\n' \
175-
"$__go_module_name" "$__go_module_file" >&2
176-
@go.print_stack_trace 1 >&2
177-
exit 1
178-
fi
179-
done
156+
# This may happen if a plugin appears more than once in a project tree.
157+
if [[ "$__go_module_file" != "$loaded_file" ]]; then
158+
@go.printf '%s\n' "WARNING: Module: $__go_module_name" \
159+
"imported at: $current_caller" \
160+
"from file: $__go_module_file" \
161+
"previously imported at: $prev_caller" \
162+
"from file: $loaded_file" >&2
163+
fi
164+
continue 2
165+
fi
166+
((++module_index))
167+
done
168+
169+
if ! _@go.import_module; then
170+
@go.printf 'ERROR: Failed to import %s module from %s at:\n' \
171+
"$__go_module_name" "$__go_module_file" >&2
172+
@go.print_stack_trace 1 >&2
173+
exit 1
174+
fi
175+
done
176+
}
180177

181-
unset __go_use_prev_caller
182-
unset __go_use_caller
183-
unset __go_module_index
184-
unset __go_loaded_file
185-
unset __go_loaded_module
186-
unset __go_module_file
187-
unset __go_module_name
178+
_@go.use_modules "$@"

lib/kcov-ubuntu

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
# This may eventually be extracted into a plugin library of its own, which is
1010
# why its exported functions don't contain a `@go.` prefix.
1111

12-
readonly __KCOV_DEV_PACKAGES=(
12+
__KCOV_DEV_PACKAGES=(
1313
'binutils-dev'
1414
'cmake'
1515
'libcurl4-openssl-dev'
@@ -18,6 +18,7 @@ readonly __KCOV_DEV_PACKAGES=(
1818
'libiberty-dev'
1919
'zlib1g-dev'
2020
)
21+
readonly __KCOV_DEV_PACKAGES
2122
readonly __KCOV_URL='https://github.com/SimonKagstrom/kcov'
2223
readonly __KCOV_VERSION='master'
2324

tests/core/plugin-scope-variable-values.bats

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ assert_scope_values_equal() {
104104
'_GO_SCRIPTS_DIR:' \
105105
"$TEST_GO_PLUGINS_DIR/first/bin" \
106106
'_GO_PLUGINS_PATHS:' \
107-
"$TEST_GO_PLUGINS_DIR/first/bin" \
108107
"$TEST_GO_PLUGINS_DIR/second/bin" \
109108
'_GO_SEARCH_PATHS:' \
110109
"$_GO_CORE_DIR/libexec" \
@@ -131,7 +130,6 @@ assert_scope_values_equal() {
131130
'_GO_SCRIPTS_DIR:' \
132131
"$TEST_GO_PLUGINS_DIR/second/bin/plugins/third/bin" \
133132
'_GO_PLUGINS_PATHS:' \
134-
"$TEST_GO_PLUGINS_DIR/second/bin/plugins/third/bin" \
135133
"$TEST_GO_PLUGINS_DIR/first/bin" \
136134
"$TEST_GO_PLUGINS_DIR/second/bin" \
137135
'_GO_SEARCH_PATHS:' \
@@ -151,7 +149,6 @@ assert_scope_values_equal() {
151149
'_GO_PLUGINS_PATHS:' \
152150
"$TEST_GO_PLUGINS_DIR/second/bin/plugins/third/bin" \
153151
"$TEST_GO_PLUGINS_DIR/first/bin" \
154-
"$TEST_GO_PLUGINS_DIR/second/bin" \
155152
'_GO_SEARCH_PATHS:' \
156153
"$_GO_CORE_DIR/libexec" \
157154
"$TEST_GO_PLUGINS_DIR/second/bin" \
@@ -173,7 +170,6 @@ assert_scope_values_equal() {
173170
'_GO_SCRIPTS_DIR:' \
174171
"$TEST_GO_PLUGINS_DIR/first/bin" \
175172
'_GO_PLUGINS_PATHS:' \
176-
"$TEST_GO_PLUGINS_DIR/first/bin" \
177173
"$TEST_GO_PLUGINS_DIR/second/bin" \
178174
'_GO_SEARCH_PATHS:' \
179175
"$_GO_CORE_DIR/libexec" \
@@ -189,7 +185,6 @@ assert_scope_values_equal() {
189185
'_GO_PLUGINS_PATHS:' \
190186
"$TEST_GO_PLUGINS_DIR/second/bin/plugins/third/bin" \
191187
"$TEST_GO_PLUGINS_DIR/first/bin" \
192-
"$TEST_GO_PLUGINS_DIR/second/bin" \
193188
'_GO_SEARCH_PATHS:' \
194189
"$_GO_CORE_DIR/libexec" \
195190
"$TEST_GO_PLUGINS_DIR/second/bin" \

0 commit comments

Comments
 (0)