Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 15 additions & 4 deletions docs/doc/zh/source_code/add_c_module.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ namespace maix::test

* 首先[编译 MaixPy 源码](./build.md) 通过,保证我们的编译环境没问题。
* 复制一份 [MaixPy/tools/maix_module](https://github.com/sipeed/MaixPy/tree/main/tools/maix_module) 工程模板到一个新的目录,可以和`MaixPy`放在同一个目录。比如将所有文件和目录复制到了`maix_xxx` 目录下。
* 在`maix_xxx`目录下,终端执行`python init_files.py`来初始化项目文件。
* 修改项目名:修改`module_name.txt` 文件,改成你要的模块名称,必须以`maix_`开头,这样方便其它用户能在 [pypi.org](https://pypi.org) 或者 [github.com](https://github.com) 搜索到你的项目。
* 在`maix_xxx`目录下,终端执行`python init_files.py`来初始化项目文件。
* 设置MaixCDK目录:`export MAIXCDK_PATH=/home/yourname/MaixCDK`
* 和 MaixPy 一样执行`python setup.py bdist_wheel linux` 就可以开始为电脑构建。
* 构建完成后可以直接在项目根目录执行`python -c "import maix_xxx;maix_xxx.basic.print('Li Hua')"`就能运行你的模块函数了。
* 执行`python setup.py bdist_wheel maixcam` 就可以为`MaixCAM` 构建软件包了。需要注意的是,构建过程种的代码提示文件(pyi文件)只能在给`linux` 平台构建的时候生成,所以在正式发布的时候需要先执行上一步的`linux`平台构建生成代码提示文件,然后再执行本步的命令生成`MaixCAM`平台的软件包。
Expand All @@ -60,9 +61,19 @@ namespace maix::test
* 当你调试好代码后,可以考虑将代码开源到[github.com](https://github.com),并且上传到[pypi.org](https://pypi.org)(具体上传方法可以看官方文档或者搜索教程,大概就是`pip install twine`然后 `twine upload dist/maix_xxx***.whl`就可以了。),写好后欢迎到[maixhub.com/share](https://maixhub.com/share)来分享告诉大家你的成果!

修改代码:
正如 [查看 MaixPy API 源码](../basic/view_src_code.md) 问种所描述的查看和修改源码的方式,在`components/maix/include` 和 `components/maix/src` 下增加源文件,增加 C++ 函数,并且填加注释,然后编译后就直接能调用了,非常简单。
比如:

正如 [查看 MaixPy API 源码](../basic/view_src_code.md) 问种所描述的查看和修改源码的方式,在`components/maix/include` 和 `components/maix/src` 下增加源文件,增加 C++ 函数,并且在对应的.hpp填加注释,然后编译后就直接能调用了,非常简单。(注意不要改main的文件)
比如,
cpp例子:
```cpp
namespace maix_xxx::test
{//直接在下面写对应的C++函数即可
int add(int a, int b)
{
return a + b;
}
} // namespace maix_xxx
```
.hpp头文件例子:
```cpp
namespace maix_xxx::test
{
Expand Down
250 changes: 187 additions & 63 deletions tools/maix_module/init_files.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,91 @@
import os
import sys

with open("module_name.txt", "r") as f:
module_name = f.readline().strip()
def main():
# ===================== 1. Read and validate module name =====================
try:
with open("module_name.txt", "r", encoding="utf-8") as f:
module_name = f.readline().strip()
except FileNotFoundError:
raise Exception("Error: module_name.txt file not found. Please create it first and fill in the module name (e.g., maix_kkk)")
except Exception as e:
raise Exception(f"Failed to read module_name.txt: {e}")

if not module_name.startswith("maix_"):
raise Exception("module name must starts with maix_")
# Validate module name must start with maix_
if not module_name.startswith("maix_"):
raise Exception("Error: Module name must start with 'maix_' (e.g., maix_kkk)")

# Optional: Specify initial version number from command line (Example: python init_files.py 1.0.1)
init_version = sys.argv[1] if len(sys.argv) > 1 else "1.0.0"
try:
version_major, version_minor, version_patch = init_version.split(".")
# Verify version number is numeric
int(version_major), int(version_minor), int(version_patch)
except ValueError:
raise Exception(f"Error: Invalid version number format (Please use format like 1.0.0, current: {init_version})")

# Create test dir, and test_import.py
def write_test_dir():
if os.path.exists("test/test_import.py"):
return
content = f"import {module_name}"
os.makedirs("test", exist_ok=True)
with open("test/test_import.py", "w", encoding="utf-8") as f:
f.write(content)
write_test_dir()
# ===================== 2. Generate test file (test/test_import.py) =====================
def write_test_import_file():
test_file = "test/test_import.py"
if os.path.exists(test_file):
print(f"Note: {test_file} already exists, skip generation")
return
try:
os.makedirs("test", exist_ok=True)
content = f"import {module_name}\nprint(f'Successfully imported module: {module_name}')"
with open(test_file, "w", encoding="utf-8") as f:
f.write(content)
print(f"Successfully generated: {test_file}")
except PermissionError:
raise Exception(f"Error: Insufficient permissions to create {test_file}")
except Exception as e:
raise Exception(f"Failed to generate test file: {e}")

# Create maix_xxx dir, write __init__.py and version.py
def write_module_dir():
init_content = f'''from .version import __version__
from ._maix_xxx import *
# ===================== 3. Generate core module files (__init__.py / version.py) =====================
def write_module_dir():
# Generate __init__.py content (Fix hardcoded extension module name)
init_content = f'''from .version import __version__
from ._{module_name} import *
'''
version_content = '''# Versions should comply with PEP440: https://peps.python.org/pep-0440/
# Generate version.py content (Dynamic version number)
version_content = f'''# Versions should comply with PEP440: https://peps.python.org/pep-0440/

version_major = 1
version_minor = 0
version_patch = 0
version_major = {version_major}
version_minor = {version_minor}
version_patch = {version_patch}

__version__ = "{}.{}.{}".format(version_major, version_minor, version_patch)
__version__ = "{version_major}.{version_minor}.{version_patch}"
'''
os.makedirs(module_name, exist_ok=True)
if not os.path.exists(f"{module_name}/__init__.py"):
with open(f"{module_name}/__init__.py", "w", encoding="utf-8") as f:
f.write(init_content)
if not os.path.exists(f"{module_name}/version.py"):
with open(f"{module_name}/version.py", "w", encoding="utf-8") as f:
f.write(version_content)
write_module_dir()
try:
# Create module directory
os.makedirs(module_name, exist_ok=True)

# Generate __init__.py
init_file = f"{module_name}/__init__.py"
if not os.path.exists(init_file):
with open(init_file, "w", encoding="utf-8") as f:
f.write(init_content)
print(f"Successfully generated: {init_file}")
else:
print(f"Note: {init_file} already exists, skip generation")

# Create compent dir and files
def write_component_dir():
cpp_content = f'''#include "{module_name}.hpp"
# Generate version.py
version_file = f"{module_name}/version.py"
if not os.path.exists(version_file):
with open(version_file, "w", encoding="utf-8") as f:
f.write(version_content)
print(f"Successfully generated: {version_file}")
else:
print(f"Note: {version_file} already exists, skip generation")
except PermissionError:
raise Exception(f"Error: Insufficient permissions to create {module_name} directory/files")
except Exception as e:
raise Exception(f"Failed to generate core module files: {e}")

namespace {module_name}::basic
{{
void hello(const std::string &name)
{{
maix::log::info(("hello: " + name).c_str());
}}
}} // namespace {module_name}
'''
hpp_content = f'''
# ===================== 4. Generate C++ extension module files (hpp/cpp) =====================
def write_component_dir():
# Generate .hpp header file content
hpp_content = f'''
#include "maix_basic.hpp"

namespace {module_name}::basic
Expand All @@ -66,25 +99,116 @@ def write_component_dir():

}} // namespace {module_name}
'''
os.makedirs("components/maix/include", exist_ok=True)
os.makedirs("components/maix/src", exist_ok=True)
if not os.path.exists(f"components/maix/include/{module_name}.hpp"):
with open(f"components/maix/include/{module_name}.hpp", "w", encoding="utf-8") as f:
f.write(hpp_content)
if not os.path.exists(f"components/maix/src/{module_name}.cpp"):
with open(f"components/maix/src/{module_name}.cpp", "w", encoding="utf-8") as f:
f.write(cpp_content)
write_component_dir()

# Create readme
def write_test_dir():
if os.path.exists("README.md"):
return
content = f"MaixPy module {module_name}\n====\n\nTODO: Add readme\n"
with open("README.md", "w", encoding="utf-8") as f:
f.write(content)
content = f"MaixPy 模块 {module_name}\n====\n\nTODO: 增加 readme\n"
with open("README_ZH.md", "w", encoding="utf-8") as f:
f.write(content)
write_test_dir()
# Generate .cpp source file content
cpp_content = f'''#include "{module_name}.hpp"

namespace {module_name}::basic
{{
void hello(const std::string &name)
{{
maix::log::info(("hello: " + name).c_str());
}}
}} // namespace {module_name}
'''
try:
# Create directories
include_dir = "components/maix/include"
src_dir = "components/maix/src"
os.makedirs(include_dir, exist_ok=True)
os.makedirs(src_dir, exist_ok=True)

# Generate .hpp file
hpp_file = f"{include_dir}/{module_name}.hpp"
if not os.path.exists(hpp_file):
with open(hpp_file, "w", encoding="utf-8") as f:
f.write(hpp_content)
print(f"Successfully generated: {hpp_file}")
else:
print(f"Note: {hpp_file} already exists, skip generation")

# Generate .cpp file
cpp_file = f"{src_dir}/{module_name}.cpp"
if not os.path.exists(cpp_file):
with open(cpp_file, "w", encoding="utf-8") as f:
f.write(cpp_content)
print(f"Successfully generated: {cpp_file}")
else:
print(f"Note: {cpp_file} already exists, skip generation")
except PermissionError:
raise Exception("Error: Insufficient permissions to create components directory/files")
except Exception as e:
raise Exception(f"Failed to generate C++ extension files: {e}")

# ===================== 5. Generate README documentation =====================
def write_readme_files():
# English README
readme_en = "README.md"
if not os.path.exists(readme_en):
en_content = f"""# MaixPy Module: {module_name}

## Quick Start
1. Compile module: `python setup.py bdist_wheel maixcam`
2. Install module: `pip install dist/{module_name}*.whl`
3. Test import: `python test/test_import.py`

## Development Guide
- Python module root: `{module_name}/`
- C++ source code: `components/maix/src/{module_name}.cpp`
- C++ header file: `components/maix/include/{module_name}.hpp`
- Version management: `{module_name}/version.py`

## TODO
- Add detailed API documentation
- Add unit tests for core functions
"""
with open(readme_en, "w", encoding="utf-8") as f:
f.write(en_content)
print(f"Successfully generated: {readme_en}")
else:
print(f"Note: {readme_en} already exists, skip generation")

# Chinese README (renamed to README_CN.md for consistency)
readme_cn = "README_CN.md"
if not os.path.exists(readme_cn):
cn_content = f"""# MaixPy Module: {module_name}

## Quick Start
1. Compile module: `python setup.py bdist_wheel 'platform'`
2. Install module: `pip install dist/{module_name}*.whl`
3. Test import: `python test/test_import.py`

## Development Guide
- Python module root directory: `{module_name}/`
- C++ source file: `components/maix/src/{module_name}.cpp`
- C++ header file: `components/maix/include/{module_name}.hpp`
- Version management file: `{module_name}/version.py`

## TODO
- Add detailed API documentation
- Add unit tests for core functions
"""
with open(readme_cn, "w", encoding="utf-8") as f:
f.write(cn_content)
print(f"Successfully generated: {readme_cn}")
else:
print(f"Note: {readme_cn} already exists, skip generation")

# ===================== Execute all generation logic =====================
write_test_import_file()
write_module_dir()
write_component_dir()
write_readme_files()

print(f"\n✅ Module project skeleton generation completed! Module name: {module_name}")
print(f"📌 Next steps:")
print(f" 1. Modify {module_name}/__init__.py to add custom interfaces")
print(f" 2. Improve components/maix/src/{module_name}.cpp to implement business logic")
print(f" 3. Execute python setup.py bdist_wheel 'platform' to compile the module")
print(f" 4. Modify whl package version: in {module_name}/version.py file")

if __name__ == "__main__":
try:
main()
except Exception as e:
print(f"\n❌ Generation failed: {e}")
sys.exit(1)
6 changes: 3 additions & 3 deletions tools/maix_module/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import platform
import shutil
import zipfile

####################################################################
# supported platforms
Expand Down Expand Up @@ -272,7 +273,7 @@ def has_ext_modules(foo):
# os.rename(os.path.join("dist", name), os.path.join("dist",
# "MaixPy-{}-{}-{}-{}.whl".format(__version__, py_tag, py_tag, platform_names[board]))
# )
if name.find("linux_riscv64") != -1:
if name.find("linux_riscv64") != -1 or name.find("manylinux2014_") != -1:# 增加linux平台判断,使linux平台和maixcam平台执行相同的whl打包逻辑
# pypi not support riscv64 yet, so we have to change to py3-none-any pkg
# unzip to dist/temp, change dist-info/WHEEL file
# zip back and rename
Expand All @@ -294,5 +295,4 @@ def has_ext_modules(foo):
shutil.rmtree("dist/temp")
os.rename(os.path.join("dist", name), os.path.join("dist",
"{}-{}-py3-none-any.whl".format(module_name, __version__))
)

)