Skip to content

Commit bcba3d0

Browse files
committed
Update
1 parent 38700da commit bcba3d0

File tree

1 file changed

+223
-0
lines changed

1 file changed

+223
-0
lines changed
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
---
2+
layout: post
3+
title: Dynamic Dependency Management for Modular Architecture
4+
date: 2024-10-26 20:44
5+
category: Guides
6+
tags: [guide, dependencies, modular, architecture, automation, python, unix]
7+
---
8+
9+
In modern software development, managing dependencies and configuring environments can often be a tedious and error-prone task. To simplify this process, we've revamped our install.sh script, which automates the installation of Python and Unix dependencies for our modular architecture. This enhanced script not only sets up a Python virtual environment but also intelligently reads configuration files to install dependencies only for active modules, streamlining the setup process.
10+
11+
## Advantages of Dynamic Dependency Management
12+
13+
1. **Modular Architecture**: Our system is designed to be modular, with each component encapsulated in a separate module. This approach allows for easier maintenance, testing, and scalability.
14+
2. **Dependency Isolation**: By specifying dependencies at the module level, we can ensure that each module has the necessary dependencies without affecting other modules.
15+
3. **Automated Installation**: The enhanced `install.sh` script automates the installation of dependencies based on the configuration files, reducing manual intervention and potential errors. Because only dependencies from active modules are installed, the setup process is optimized and efficient.
16+
17+
## Background
18+
19+
To handle both **Python** and **Unix (system)** dependencies, we’ll make a few updates:
20+
21+
1. **Add a `dependencies` section** to each module's YAML configuration for listing Unix dependencies.
22+
2. Use **Python** (specifically the `yaml` library) in the `install.sh` script to read dependencies from each config file.
23+
3. Ensure **`apt-get install`** is used for Unix dependencies, and **`pip install`** for Python dependencies.
24+
25+
Here's what this would look like:
26+
27+
### Updated YAML Config Files
28+
29+
Each module config can now specify both Python and Unix dependencies.
30+
31+
**Example `servos.yml`:**
32+
33+
```yaml
34+
servos:
35+
port: /dev/ttyAMA0
36+
conf:
37+
leg_l_hip: { id: 0, pin: 9, range: [0, 180], start: 40 }
38+
# other servo configurations...
39+
dependencies:
40+
python:
41+
- pyserial
42+
- pigpio
43+
unix:
44+
- libpigpio-dev
45+
```
46+
47+
**Example `motion.yml`:**
48+
49+
```yaml
50+
motion:
51+
pin: 26
52+
dependencies:
53+
python:
54+
- RPi.GPIO
55+
unix:
56+
- wiringpi
57+
```
58+
59+
### Modified `install.sh` Script
60+
61+
The install script below:
62+
1. Parses `python` and `unix` dependencies from each module's YAML file.
63+
2. Installs Unix dependencies with `apt-get install` and Python dependencies with `pip install`.
64+
3. Uses a **Python helper** embedded within the script to read YAML files (using `pyyaml`).
65+
66+
Here’s the modified `install.sh` script:
67+
68+
```bash
69+
#!/bin/bash
70+
71+
# Set up Python virtual environment
72+
python3 -m venv --system-site-packages myenv
73+
source myenv/bin/activate
74+
75+
# Initialize arrays for dependencies and active module names
76+
PYTHON_DEPENDENCIES=()
77+
UNIX_DEPENDENCIES=()
78+
ACTIVE_MODULES=()
79+
80+
# Helper function to parse dependencies from YAML files using Python
81+
parse_dependencies() {
82+
myenv/bin/python3 - <<EOF
83+
import yaml, sys, os
84+
85+
config_file = "$1"
86+
module_name = os.path.basename(config_file).replace('.yml', '') # Get the module name from the filename
87+
try:
88+
with open(config_file) as f:
89+
config = yaml.safe_load(f)
90+
# Check if the top level of the config is a dictionary to avoid AttributeError
91+
if isinstance(config, dict):
92+
for section in config.values():
93+
# Check if module is active before parsing dependencies
94+
if isinstance(section, dict) and section.get('enabled') is True:
95+
print(f"MODULE:{module_name}")
96+
if 'dependencies' in section:
97+
for dep_type, deps in section['dependencies'].items():
98+
if dep_type == 'python':
99+
for dep in deps:
100+
print(f"PYTHON:{dep}")
101+
elif dep_type == 'unix':
102+
for dep in deps:
103+
print(f"UNIX:{dep}")
104+
except yaml.YAMLError as e:
105+
print(f"Error reading {config_file}: {e}", file=sys.stderr)
106+
EOF
107+
}
108+
109+
# Iterate over each YAML config file in the config directory
110+
for config_file in config/*.yml; do
111+
while IFS= read -r dependency; do
112+
# Separate Python and Unix dependencies and capture active module names
113+
if [[ $dependency == MODULE:* ]]; then
114+
ACTIVE_MODULES+=("${dependency#MODULE:}")
115+
elif [[ $dependency == PYTHON:* ]]; then
116+
PYTHON_DEPENDENCIES+=("${dependency#PYTHON:}")
117+
elif [[ $dependency == UNIX:* ]]; then
118+
UNIX_DEPENDENCIES+=("${dependency#UNIX:}")
119+
fi
120+
done < <(parse_dependencies "$config_file")
121+
done
122+
123+
# Remove duplicate dependencies
124+
UNIQUE_PYTHON_DEPENDENCIES=($(echo "${PYTHON_DEPENDENCIES[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
125+
UNIQUE_UNIX_DEPENDENCIES=($(echo "${UNIX_DEPENDENCIES[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
126+
UNIQUE_ACTIVE_MODULES=($(echo "${ACTIVE_MODULES[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
127+
128+
# Update apt-get and install Unix dependencies
129+
if [ ${#UNIQUE_UNIX_DEPENDENCIES[@]} -ne 0 ]; then
130+
sudo apt-get update
131+
for dep in "${UNIQUE_UNIX_DEPENDENCIES[@]}"; do
132+
sudo apt-get install -y "$dep"
133+
done
134+
fi
135+
136+
# Install Python dependencies explicitly using the virtual environment's pip
137+
for dep in "${UNIQUE_PYTHON_DEPENDENCIES[@]}"; do
138+
myenv/bin/python3 -m pip install "$dep"
139+
done
140+
141+
# Set execute permissions for additional scripts
142+
chmod 777 startup.sh stop.sh
143+
144+
# Summary of modules and dependencies installed
145+
echo -e "\n==== Installation Summary ===="
146+
echo "Active modules installed: ${#UNIQUE_ACTIVE_MODULES[@]}"
147+
for module in "${UNIQUE_ACTIVE_MODULES[@]}"; do
148+
echo " - $module"
149+
done
150+
151+
echo -e "\nPython dependencies installed:"
152+
for dep in "${UNIQUE_PYTHON_DEPENDENCIES[@]}"; do
153+
echo " - $dep"
154+
done
155+
156+
echo -e "\nUnix dependencies installed:"
157+
for dep in "${UNIQUE_UNIX_DEPENDENCIES[@]}"; do
158+
echo " - $dep"
159+
done
160+
echo "============================="
161+
162+
```
163+
164+
### Explanation of Changes
165+
166+
1. **`parse_dependencies` Function**: A Python function is embedded in the `install.sh` script to parse YAML files and print dependencies in a formatted way for easy processing.
167+
2. **Dependency Arrays**:
168+
- `PYTHON_DEPENDENCIES` and `UNIX_DEPENDENCIES` store dependencies, allowing us to separate out `apt-get` and `pip` installations.
169+
3. **Removing Duplicates**: Using `sort -u` to ensure dependencies aren’t installed more than once.
170+
4. **Installing Dependencies**:
171+
- **Unix** dependencies are updated and installed using `apt-get`.
172+
- **Python** dependencies are installed with `pip`.
173+
174+
### Folder Structure Example
175+
176+
Here’s the directory structure for this configuration:
177+
178+
```plaintext
179+
project_root/
180+
├── config/
181+
│ ├── servos.yml
182+
│ ├── motion.yml
183+
├── install.sh
184+
└── main.py
185+
```
186+
187+
This approach ensures that dependencies are handled based on each module’s needs, making it easier to maintain and update dependencies dynamically.
188+
189+
### Example Output
190+
191+
After running the `install.sh` script, you should see a summary of the installed modules and dependencies.
192+
193+
Remember, only **active modules** will have their dependencies installed.
194+
195+
```plaintext
196+
==== Installation Summary ====
197+
Active modules installed: 12
198+
- animate
199+
- braillespeak
200+
- buzzer
201+
- motion
202+
- neopixel
203+
- piservo
204+
- pitemperature
205+
- serial
206+
- servos
207+
- tracking
208+
- translator
209+
- vision
210+
211+
Python dependencies installed:
212+
- adafruit-circuitpython-seesaw
213+
- googletrans==3.1.0a0
214+
- gpiozero
215+
- pigpio
216+
- pypubsub
217+
- python3-munkres
218+
- python3-opencv
219+
220+
Unix dependencies installed:
221+
- imx500-all
222+
=============================
223+
```

0 commit comments

Comments
 (0)