How to Structure Projects
Our software team has developed some standard project structures over the years for both Python and C++. Following these project structures make it easy to identify how to run code, as well as where to find files.
These tips are broadly applicable to projects outside of the team as well, but should be followed as rules within the team repositories.
Python Project Structure
The standard Python package structure is adapted from the Hitchhiker’s Guide to Python.
A standard AmadorUAVs Python project looks like this:
photographer/
| README.md
| LICENSE (optional)
| setup.py
| install.sh (additional non-Python deps)
| requirements.txt
| env/ (created by virtualenv)
| photographer/__init__.py
| photographer/drone.py
| photographer/server.py
| photographer/__main__.py
(docs are located in the project wiki)
Let’s break these down:
README.mdis a file explaining the basics of what’s in the project. These should include a short description, instructions for installing and running the project, and (if applicable) examples of how to use it for libraries.LICENSEis optional. Throw in an MIT license text if you’re not sure.setup.pyis the setup script. This is important for libraries to know how to be installed. See the qgcwrite setup.py for examples of how to use it.install.shis the install script. It is optional, but should contain instructions for installing additional non-Python dependencies if necessary.requirements.txtis the dependencies file. You should put all necessary Python libraries needed to run the program in here. If you are using a virtualenv (which you should), you canpip freeze > requirements.txtto dump all installed dependencies into the requirements file.env/is the virtual environment. This folder should NOT BE COMMITTED to Git. See here for a guide on how to use virtualenvs.- The main code should go under a Python package with the same name as the project (e.g. photographer).
- Under the package, an
__init__.pyshould be created so that Python recognizes it as a package. - A
__main__.pyshould also be created. This is the file that will be run when we run the package. - To import, we need to import from the package: e.g.
import photographer.droneinstead of justimport drone. This seems tedious but it makes the code more organized and portable in the long run.
For an example of this structure in action, see any of our Python packages.
To set up and run a standard Python project:
- Clone
python3 -m venv envsource env/bin/activatepip3 install -r requirements.txt./install.shif necessarypython3 -m photographerto run the package. This will executephotographer/__main__.py.- If the project is a library,
python3 setup.py installto install the package.
Even if the project is a library, it should include a __main__.py example for testing and running.
C++ Project Structure
A standard AmadorUAVs C++ project looks like this:
pilot/
| README.md
| LICENSE (optional)
| install.sh (dependencies)
| meson.build (build file) OR
| Makefile (if meson is not applicable)
| include/pilot/otherstuff.h
| include/pilot/otherstuff2.h
| src/main.cpp
| src/otherstuff.cpp
| src/otherstuff2.cpp
(docs are located in the project wiki)
AmadorUAVs uses the Meson build system by default. If it is not compatible, CMake or a plain Makefile may be substituted.
If a raw Makefile is used, it should have the following targets at minimum:
makemake installmake run(if applicable)
Header files should be under include/<projectname>/<file.h>, and match with a source C/C++ file of the same name.
Main function should be in a file named main.cpp.
Note that there are many cases in which the standard C++ structure is not completely compatible (e.g. in Zephyr, where CMake is necessary). In these cases, make necessary modifications to the structure to fit the framework.