-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmacros.bash
407 lines (359 loc) · 14.1 KB
/
macros.bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# Included by ./setup.bash
# Executes colcon build in workspace.
build() {
local prev_dir=$(pwd)
cd $_KALMAN_WS_ROOT
# Select packages to build.
# If no arguments are provided, build all packages.
local pkg_names=""
local pkg_paths=""
while IFS=: read -r name path; do
local pkg_names="$pkg_names $name"
local pkg_paths="$pkg_paths $path"
done < <(python3 "$_KALMAN_WS_ROOT/scripts/select_colcon_packages.py" "$@")
local selected_packages=false
if [ $# -ne 0 ]; then
local selected_packages=true
fi
# If no packages are found, show an error messeage and return.
if [ -z "$pkg_names" ]; then
echo "The query did not match any packages."
cd $prev_dir
return
fi
# Display which packages will be built if filtering is used.
if [ $selected_packages = true ]; then
echo "Packages to build:"
for pkg in $pkg_names; do
echo ' -' $pkg
done
fi
# Install rosdep dependencies.
mkdir -p $HOME/.cache/kalman_ws
local can_skip_rosdep_json=$(python3 $_KALMAN_WS_ROOT/scripts/can_skip_install.py --marker-file=rosdep_mod_times.json --trigger-file=package.xml $pkg_paths)
if [ -n "$can_skip_rosdep_json" ]; then
echo "Installing rosdep dependencies..."
rosdep install --rosdistro $ROS_DISTRO --default-yes --ignore-packages-from-source --from-paths $pkg_paths
if [ $? -ne 0 ]; then
echo "Failed to install rosdep dependencies."
cd $prev_dir
return
fi
# Update marker file.
echo "$can_skip_rosdep_json" > $HOME/.cache/kalman_ws/rosdep_mod_times.json
fi
# Install additional APT dependencies.
# Those are located in the apt_packages.txt file in each package.
local can_skip_apt_json=$(python3 $_KALMAN_WS_ROOT/scripts/can_skip_install.py --marker-file=apt_mod_times.json --trigger-file=apt_packages.txt $pkg_paths)
if [ -n "$can_skip_apt_json" ]; then
echo "Installing custom APT dependencies..."
local installed_apt_ids=$(apt list --installed 2>/dev/null | cut -d '/' -f 1)
for pkg_path in $pkg_paths; do
if [ -f "$pkg_path/apt_packages.txt" ]; then
# Read apt_packages.txt.
local apt_ids=$(cat $pkg_path/apt_packages.txt)
# For each package name, check if it is installed.
# If not, install it.
for apt_id in $apt_ids; do
# Check if apt_id is in installed_apt_ids.
# installed_apt_ids="package-1 package-2 ..."
if [[ $installed_apt_ids != *"$apt_id"* ]]; then
echo "Installing $apt_id..."
sudo apt install -y $apt_id
if [ $? -ne 0 ]; then
echo "Failed to install $apt_id."
cd $prev_dir
return
fi
fi
done
fi
done
echo "$can_skip_apt_txt" > $HOME/.cache/kalman_ws/apt_mod_times.json
fi
# Install additional PIP dependencies.
# Those are located in the requirements.txt file in each package.
local can_skip_pipe_json=$(python3 $_KALMAN_WS_ROOT/scripts/can_skip_install.py --marker-file=pip_mod_times.json --trigger-file=requirements.txt $pkg_paths)
if [ -n "$can_skip_pipe_json" ]; then
echo "Installing custom PIP dependencies..."
local installed_pip_ids=$(pip freeze 2>/dev/null | cut -d '=' -f 1)
for pkg_path in $pkg_paths; do
if [ -f "$pkg_path/requirements.txt" ]; then
# Read requirements.txt.
local pip_ids=$(cat $pkg_path/requirements.txt | sed 's/\s*#.*//g')
# For each package name, check if it is installed.
# If not, install it.
for pip_id in $pip_ids; do
# Check if PIP_ID is in installed_pip_ids.
# installed_pip_ids="package-1 package-2..."
local pip_id_without_version=$(echo $pip_id | cut -d '=' -f 1 | cut -d '>' -f 1 | cut -d '<' -f 1)
if [[ $installed_pip_ids != *"$pip_id_without_version"* ]]; then
echo "Installing $pip_id..."
pip install $pip_id
if [ $? -ne 0 ]; then
echo "Failed to install $pip_id."
cd $prev_dir
return
fi
fi
done
fi
done
echo "$can_skip_pipe_json" > $HOME/.cache/kalman_ws/pip_mod_times.json
fi
# Build the workspace.
echo "Building packages..."
colcon build --symlink-install --base-paths src --packages-select $pkg_names --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=1 --no-warn-unused-cli
if [ $? -ne 0 ]; then
echo "Failed to build some packages."
cd $prev_dir
return
fi
# Load .vscode/settings.json.
echo "Updating Visual Studio Code settings..."
python3 $_KALMAN_WS_ROOT/scripts/configure_vscode.py
# Source setup scripts.
source $_KALMAN_WS_ROOT/scripts/source-ros-setups.bash
echo "Done."
cd $prev_dir
}
# Removes build artifacts in workspace.
clean() {
local prev_dir=$(pwd)
cd $_KALMAN_WS_ROOT
# Select packages to build.
# If no arguments are provided, build all packages.
local pkg_names=""
local pkg_paths=""
while IFS=: read -r name path; do
local pkg_names="$pkg_names $name"
local pkg_paths="$pkg_paths $path"
done < <(python3 "$_KALMAN_WS_ROOT/scripts/select_colcon_packages.py" "$@" kalman_ws_disable_recursive_dependencies_in_selection)
local selected_packages=false
if [ $# -ne 0 ]; then
local selected_packages=true
fi
# If no packages are found, show an error messeage and return.
if [ -z "$pkg_names" ]; then
echo "The query did not match any packages."
cd $prev_dir
return
fi
# Display which packages will be cleared if queries are used.
if [ $selected_packages = true ]; then
echo "Packages to clean:"
for pkg in $pkg_names; do
echo ' -' $pkg
done
fi
if [ $selected_packages = false ]; then
# If not arguments were provided, perform a full wipe.
rm -rf $_KALMAN_WS_ROOT/build
rm -rf $_KALMAN_WS_ROOT/install
rm -rf $_KALMAN_WS_ROOT/log
else
# Remove build artifacts for selected packages only.
for pkg_name in $pkg_names; do
rm -rf $_KALMAN_WS_ROOT/build/$pkg_name
rm -rf $_KALMAN_WS_ROOT/install/$pkg_name
done
fi
# Remove paths from $AMENT_PREFIX_PATH and $CMAKE_PREFIX_PATH.
unset AMENT_PREFIX_PATH
unset CMAKE_PREFIX_PATH
source $_KALMAN_WS_ROOT/scripts/source-ros-setups.bash
echo "Done."
cd $prev_dir
}
# Auto-formats all supported file types in the workspace.
format() {
local prev_dir=$(pwd)
cd $_KALMAN_WS_ROOT
# Add Python binaries to the PATH if they are not already there.
if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
export PATH=$HOME/.local/bin:$PATH
fi
# Ensure that clang-format and black are installed from PyPI.
# Use PIP to check for that because clang-format could have been installed from APT.
local pkgs=$(pip3 freeze)
if ! echo $pkgs | grep -q ' clang-format=='; then
echo "Installing clang-format..."
pip3 install --user clang-format
fi
if ! echo $pkgs | grep -q ' black=='; then
echo "Installing black..."
pip3 install --user black
fi
# Find Python files to format and run black on them.
local python_files=$(find src -name '*.py')
if [ ! -z "$python_files" ]; then
echo "Formatting Python files:"
black $python_files
echo
fi
# Find C++ files to format and run clang-format on them.
local cpp_files=$(find src -name '*.cpp' -o -name '*.hpp' -o -name '*.c' -o -name '*.h')
if [ ! -z "$cpp_files" ]; then
echo "Formatting C++ files:"
for file in $cpp_files; do
# Check if any upper directory contains an AMENT_IGNORE or COLCON_IGNORE file.
# If it does, skip the file.
local dir=$(dirname "$file")
while [ "$dir" != "." ] && [ "$dir" != "/" ]; do
if [ -e "$dir/AMENT_IGNORE" ] || [ -e "$dir/COLCON_IGNORE" ]; then
continue 2
fi
local dir=$(dirname "$dir")
done
echo "Formatting $(basename $file)..."
clang-format -i $file
done
echo
fi
echo "Done."
cd $prev_dir
}
# Runs Colcon tests and linters in the workspace.
# This macro cannot be named 'test' because it conflicts with the built-in test command in Bash.
lint() {
local prev_dir=$(pwd)
cd $_KALMAN_WS_ROOT
# Select packages to test.
# If no arguments are provided, select all packages.
# See the same code in build() for additional comments.
local pkg_info=$(colcon list --base-paths src | grep -oP '\S+\s\S+(?=\s)')
local pkg_info=($pkg_info)
local pkg_names=""
local pkg_paths=""
if [ $# -ne 0 ]; then
local queries="$@"
for ((i = 0; i < ${#pkg_info[@]}; i += 2)); do
local name=${pkg_info[$i]}
local path=${pkg_info[$i + 1]}
for query in $queries; do
if [[ $name == *"$query"* ]]; then
local pkg_names="$pkg_names $name"
local pkg_paths="$pkg_paths $path"
break
fi
done
done
fi
local pkg_names=$(echo $pkg_names | sed 's/^ //')
local pkg_paths=$(echo $pkg_paths | sed 's/^ //')
echo "Running tests:"
# Throw if workspace has not been built.
if [ ! -d "build" ]; then
echo "Workspace has not been built yet. Run 'build' first."
cd $prev_dir
return
fi
# Run Colcon test.
if [ -z "$pkg_names" ]; then
colcon test --base-paths src
else
colcon test --base-paths src --packages-select $pkg_names
fi
# TODO: Somethow detect test failure and provide results for only the selected packages.
echo "Done."
cd $prev_dir
}
# Finds the largest objects current Git repository.
git-du() {
# Create a table with rows formatted like so: "<hash> <size> <path>"
local table="$(
git rev-list --objects --all |
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
sed -n 's/^blob //p' |
sort --numeric-sort --key=2 |
cut -c 1-12,41- |
$(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
)"
# Display the 10 most space-consuming objects.
echo ...
echo "$table" | tail -n 10
# Create a sum expression from the sizes.
local exp="$(echo "$table" | awk '{ print $2 }' | paste -sd+ -)"
# example exp: 923B+927B+974B+983B+1017B+1.0KiB+1.0KiB+1.1KiB+1.1KiB+1.1KiB
# Convert human-readable sizes to byte counts and then evaluate the sum.
local exp="$(echo "$exp" | sed 's/GiB/*1073741824/g' | sed 's/MiB/*1048576/g' | sed 's/KiB/*1024/g' | sed 's/B//g')"
local total="$(echo "$exp" | bc)"
# Count all files.
local num_files="$(echo "$table" | wc -l)"
# Display the total size and the number of files.
echo "$(echo "$total" | $(command -v gnumfmt || echo numfmt) --to=iec-i --suffix=B --padding=20) total ($num_files files)"
# Find the size of the pack files and convert it to byte count.
local pack_total_hr="$(git count-objects -vH | grep -E 'size-pack' | sed 's/.*size-pack: //')"
local pack_total="$(echo "$pack_total_hr" | sed 's/GiB/*1073741824/g' | sed 's/MiB/*1048576/g' | sed 's/KiB/*1024/g' | sed 's/B//g' | bc)"
# Display the size of the pack files.
echo "$(echo "$pack_total" | $(command -v gnumfmt || echo numfmt) --to=iec-i --suffix=B --padding=20) of pack files (represents download size only if no other branches were checked out since cloning)"
}
# Resets all submodules to main and pulls any recent changes.
reset-pull() {
# Display a warning Y/N prompt.
echo "This will reset all submodules to main and pull any recent changes. YOU WILL LOSE ALL LOCAL CHANGES."
read -p "Are you sure you want to continue? (Y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
return
fi
echo
local prev_dir=$(pwd)
cd $_KALMAN_WS_ROOT
# Find all submodules.
local REPOS=$(find ./src/ -name .git -execdir pwd \;)
# # checkout
# echo "$REPOS" | xargs -P 8 -I {} git -C {} checkout main
# # reset
# echo "$REPOS" | xargs -P 8 -I {} git -C {} reset --hard origin/main
# # pull
# find ./src/ -name .git -execdir pwd \; | xargs -P 8 -I {} git -C {} pull
echo "Checking out on main..."
for REPO in $REPOS; do
cd $REPO
printf "$(basename $REPO) "
local git_out=$(git checkout main 2>&1)
if [ $? -ne 0 ]; then
echo "Failed to checkout on main:\n$git_out"
cd $prev_dir
return
fi
done
echo
echo
echo "Resetting to origin/main..."
for REPO in $REPOS; do
cd $REPO
printf "$(basename $REPO) "
local git_out=$(git reset --hard origin/main 2>&1)
if [ $? -ne 0 ]; then
echo "Failed to reset to origin/main:\n$git_out"
cd $prev_dir
return
fi
done
echo
echo
echo "Pulling changes..."
for REPO in $REPOS; do
cd $REPO
printf "$(basename $REPO) "
local git_out=$(git pull 2>&1)
if [ $? -ne 0 ]; then
echo "Failed to pull changes:\n$git_out"
cd $prev_dir
return
fi
done
echo
echo
# Print current commit hashes and statuses.
echo "Currently at:"
for REPO in $REPOS; do
cd $REPO
printf "$(basename $REPO) - "
local git_out=$(git log -1 --oneline 2>&1)
echo $git_out
done
cd $prev_dir
}