Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Tests

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.14

- name: Install dependencies
run: |
pip install -e .
pip install pytest

- name: Run tests
run: pytest -v
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ dist/

mama/mama.spec
mama.cmake

packages/
bin/
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Each mama build target exports CMake `${ProjectName}_INCLUDES` and `${ProjectNam
are gathered in correct linker order inside `MAMA_INCLUDES` and `MAMA_LIBS`. This ensures the least
amount of friction for developers - everything just works.

There is no central package repository, all packages are pulled and updated from public or
There is no central package repository, all packages are pulled and updated from public or
private git repositories. Package versioning is done through git tags or branches.

Custom build systems are also supported. For additional documentation explore: [build_target.py](mama/build_target.py)
Expand Down Expand Up @@ -219,7 +219,7 @@ And then rebuilding with an artifactory package available
$ mama rebuild googletest
========= Mama Build Tool ==========
- Target googletest CLEAN linux
- Target googletest BUILD [cleaned target]
- Target googletest BUILD [cleaned target]
Artifactory fetch ftp.myartifactory.com/googletest-linux-x64-release-ebb36f3 770.6KB
|<==================================================| 100 %
Artifactory unzip googletest-linux-x64-release-ebb36f3
Expand All @@ -233,11 +233,29 @@ $ mama rebuild googletest
## For Mama Contributors
We are open for any improvements and feedback via pull requests.

### Development Setup
The package `setuptools>=65.0` is required, ensure the version is correct with `pip3 show setuptools`.

You can set up local development with `$ pip3 install -e . --no-cache-dir` but make sure you have latest setuptools (>65.0) and latest pip3 (>22.3). This command will fail with older toolkits.

Uploading a source distributionP:
### Running Tests

Install pytest and run all tests from the project root:

```bash
uv venv
uv pip install pytest
uv run pytest
```

Or to run a specific test:

```bash
pytest tests/test_git_pinning/
```

### Publishing
Uploading a source distribution:
1. Get dependencies: `pip3 install build twine`
2. Build sdist: `python -m build`
3. Upload with twine: `twine upload --skip-existing dist/*`
Expand Down
71 changes: 37 additions & 34 deletions mama/build_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ class BuildTarget:
Customization points:
```
class MyProject(mama.BuildTarget):

workspace = 'build'

def configure(self):
self.add_git('ReCpp',
self.add_git('ReCpp',
'http://github.com/RedFox20/ReCpp.git')

def configure(self):
Expand Down Expand Up @@ -129,9 +129,9 @@ def source_dir(self, subpath=''):
"""
Returns the current source directory.
```
self.source_dir()
self.source_dir()
# --> C:/Projects/ReCpp
self.source_dir('lib/ReCpp.lib')
self.source_dir('lib/ReCpp.lib')
# --> C:/Projects/ReCpp/lib/ReCpp.lib
```
"""
Expand All @@ -143,7 +143,7 @@ def build_dir(self, subpath=''):
"""
Returns the current build directory.
```
self.build_dir()
self.build_dir()
# --> C:/Projects/ReCpp/build/windows
self.build_dir('lib/ReCpp.lib')
# --> C:/Projects/ReCpp/build/windows/lib/ReCpp.lib
Expand Down Expand Up @@ -201,12 +201,12 @@ def add_local(self, name, source_dir, mamafile=None, always_build=False, args=[]
return self.dep.add_child(LocalSource(name, source_dir, mamafile, always_build, args))


def add_git(self, name, git_url, git_branch='', git_tag='', mamafile=None, shallow=True, args=[]) -> BuildDependency:
def add_git(self, name, git_url, git_branch='', git_tag='', git_commit='', mamafile=None, shallow=True, args=[]) -> BuildDependency:
"""
Add a remote GIT dependency.
The dependency will be cloned and updated according to mamabuild.
Use `mama update` to force update the git repositories.

If the remote GIT repository does not contain a `mamafile.py`, you will have to
provide your own relative or absolute mamafile path.

Expand All @@ -216,12 +216,16 @@ def add_git(self, name, git_url, git_branch='', git_tag='', mamafile=None, shall
```
self.add_git('ReCpp', 'git@github.com:RedFox20/ReCpp.git')
self.add_git('ReCpp', 'git@github.com:RedFox20/ReCpp.git', git_branch='master')
self.add_git('opencv', 'https://github.com/opencv/opencv.git',
self.add_git('opencv', 'https://github.com/opencv/opencv.git',
git_branch='3.4', mamafile='mama/opencv_cfg.py')
```
"""
if self.dep.from_artifactory: # already loaded from artifactory?
return self.get_dependency(name)

if git_tag == '' and git_commit != '':
git_tag = git_commit

return self.dep.add_child(Git(name, git_url, git_branch, git_tag, mamafile, shallow, args))


Expand Down Expand Up @@ -301,7 +305,7 @@ def inject_products(self, dst_dep, src_dep, include_path, libs, libfilters=None)
Name of defines is given via `include_path` and `libs` params.
`libfilters` does simple string matching; if nothing matches, the first export lib is chosen.
```
self.inject_products('libpng', 'zlib',
self.inject_products('libpng', 'zlib',
'ZLIB_INCLUDE_DIR', 'ZLIB_LIBRARY',
'zlibstatic')
```
Expand All @@ -326,7 +330,7 @@ def get_product_defines(self):
Returns a list of injected defines:
```
defines = self.get_product_defines()
# --> [ 'ZLIB_INCLUDE_DIR=path/to/zlib/include',
# --> [ 'ZLIB_INCLUDE_DIR=path/to/zlib/include',
# 'ZLIB_LIBRARY=path/to/lib/zlib.a', ... ]
```
"""
Expand Down Expand Up @@ -378,9 +382,9 @@ def add_build_dependency(self, all=None, windows=None, linux=None, macos=None, i
Manually add a build dependency to prevent unnecessary rebuilds.

@note Normally the build dependency is detected from the packaged libraries.

if the dependency file does not exist, then the project will be rebuilt

if your project has no build dependencies, it will always be rebuilt, so make sure
to add_build_dependency or export_lib
```
Expand Down Expand Up @@ -425,7 +429,7 @@ def package(self):
def export_include(self, include_path, build_dir=False):
"""
CUSTOM PACKAGE INCLUDES (if self.default_package() is insufficient).

Export include path relative to source directory OR if build_dir=True, then relative to build directory.
```
self.export_include('include') # MyRepo/include
Expand All @@ -440,7 +444,7 @@ def export_include(self, include_path, build_dir=False):
def export_includes(self, include_paths=[''], build_dir=False):
"""
CUSTOM PACKAGE INCLUDES (if self.default_package() is insufficient)

Export include paths relative to source directory
OR if build_dir=True, then relative to build directory
Example:
Expand All @@ -455,7 +459,7 @@ def export_includes(self, include_paths=[''], build_dir=False):
def export_lib(self, relative_path, src_dir=False, build_dir=True):
"""
CUSTOM PACKAGE LIBS (if self.default_package() is insufficient)

Export a specific lib relative to build directory
OR if src_dir=True, then relative to source directory
Example:
Expand All @@ -472,17 +476,17 @@ def export_lib(self, relative_path, src_dir=False, build_dir=True):
def export_libs(self, path = '.', pattern_substrings = ['.lib', '.a'], src_dir=False, build_dir=True, order=None):
"""
CUSTOM PACKAGE LIBS (if self.default_package() is insufficient)

Export several libs relative to build directory using EXTENSION MATCHING
OR if src_dir=True, then relative to source directory

Example:
```
self.export_libs() # gather any .lib or .a from build dir
self.export_libs('.', ['.dll', '.so']) # gather any .dll or .so from build dir
self.export_libs('lib', src_dir=True) # export everything from project/lib directory
self.export_libs('external/lib') # gather specific static libs from build dir

# export the libs in a particular order for Linux linker
self.export_libs('lib', order=[
'xphoto', 'calib3d', 'flann', 'core'
Expand All @@ -501,7 +505,7 @@ def export_asset(self, asset, category=None, src_dir=True, build_dir=False):
This can be later used when creating a deployment

category -- (optional) Can be used for grouping the assets and flattening folder structure

Example:
```
self.export_asset('extras/csharp/NanoMesh.cs')
Expand All @@ -522,12 +526,12 @@ def export_assets(self, assets_path: str, pattern_substrings = [], category=None
This can be later used when creating a deployment

category -- (optional) Can be used for grouping the assets and flattening folder structure

Example:
```
self.export_assets('extras/csharp', ['.cs'])
--> {deploy}/extras/csharp/NanoMesh.cs

self.export_assets('extras/csharp', ['.cs'], category='dotnet')
--> {deploy}/dotnet/NanoMesh.cs
```
Expand Down Expand Up @@ -563,7 +567,7 @@ def inject_env(self):
```
def build(self):
self.inject_env() # prepare platform
self.my_custom_build() #
self.my_custom_build() #
```
"""
cmake.inject_env(self)
Expand Down Expand Up @@ -614,7 +618,7 @@ def add_c_flags(self, *flags):
for flag in flags:
if isinstance(flag, list): self.add_c_flags(*flag)
else: self._add_dict_flag(self.cmake_cflags, flag)


def add_cl_flags(self, *flags):
"""
Expand Down Expand Up @@ -670,7 +674,7 @@ def add_platform_ld_flags(self, windows=None, linux=None, macos=None, ios=None,
Adds linker flags depending on configuration platform.
Supports many different usages: strings, list of strings, or space separate string.
```
self.add_platform_ld_flags(windows='/LTCG',
self.add_platform_ld_flags(windows='/LTCG',
ios=['-lobjc', '-rdynamic'],
linux='-rdynamic -s')
```
Expand Down Expand Up @@ -803,7 +807,7 @@ def copy(self, src: str, dst: str, filter: list = None):
Utility for copying files and folders
```
# copies built .so into an android archive
self.copy(self.build_dir('libAwesome.so'),
self.copy(self.build_dir('libAwesome.so'),
self.source_dir('deploy/Awesome.aar/jni/armeabi-v7a'))
```
- filter: can be a string or list of strings to filter files by suffix
Expand Down Expand Up @@ -859,7 +863,7 @@ def download_and_unzip(self, remote_zip: str, extract_dir: str, unless_file_exis

unless_file_exists -- If the specified file exists, then download and unzip steps are skipped.
```
self.download_and_unzip('http://example.com/archive.zip',
self.download_and_unzip('http://example.com/archive.zip',
'bin', 'bin/unzipped_file.txt')
# --> 'bin/' on success
# --> None on failure
Expand Down Expand Up @@ -982,7 +986,7 @@ def run_program(self, working_dir: str, command: str, exit_on_fail=True, env=Non
"""
Run any program in any directory. Can be used for custom tools.
```
self.run_program(self.source_dir('bin'),
self.run_program(self.source_dir('bin'),
self.source_dir('bin/DbTool'))
```
"""
Expand Down Expand Up @@ -1019,7 +1023,7 @@ def gtest(self, executable: str, args: str, src_dir=True, gdb=False):
The gtest report is written to $src_dir/test/report.xml.
Arguments
- executable -- which executable to run
- args -- a string of options separated by spaces,
- args -- a string of options separated by spaces,
'gdb', 'nogdb' or gtest fixture/test partial name
- src_dir -- [True] If true, then executable is relative to source directory.
- gdb -- [False] If true, then run with gdb.
Expand Down Expand Up @@ -1133,8 +1137,8 @@ def package(self):
# custom export AGL as include from source folder
self.export_includes(['AGL'])
# custom export any .lib or .a from build folder
self.export_libs('.', ['.lib', '.a'])
self.export_libs('.', ['.lib', '.a'])

if self.windows:
self.export_syslib('opengl32.lib')

Expand All @@ -1143,7 +1147,7 @@ def package(self):
```
"""
pass


def default_package(self):
"""
Expand Down Expand Up @@ -1235,7 +1239,7 @@ def papa_deploy(self, package_path, src_dir=False,
MyPackageName/libawesome.so
MyPackageName/include/...
MyPackageName/someassets/extra.txt

PAPA descriptor `papa.txt` format:
P MyPackageName
I include
Expand Down Expand Up @@ -1281,7 +1285,7 @@ def start(self, args):
"""
pass


############################################


Expand Down Expand Up @@ -1502,4 +1506,3 @@ def build(self):


######################################################################################

Loading