hatchit 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hatchit/__init__.py +13 -0
- hatchit/cli.py +32 -0
- hatchit/py.typed +0 -0
- hatchit/scaffold.py +340 -0
- hatchit/templates/docs/Makefile +29 -0
- hatchit/templates/docs/_static/.gitignore +1 -0
- hatchit/templates/docs/authors.md +4 -0
- hatchit/templates/docs/changelog.md +4 -0
- hatchit/templates/docs/conf.py +333 -0
- hatchit/templates/docs/contributing.md +4 -0
- hatchit/templates/docs/index.md +39 -0
- hatchit/templates/docs/license.md +5 -0
- hatchit/templates/docs/readme.md +4 -0
- hatchit/templates/docs/requirements.txt +9 -0
- hatchit/templates/github/dependabot.yml +15 -0
- hatchit/templates/github_workflows/publish-pypi.yml +52 -0
- hatchit/templates/github_workflows/run-tests.yml +73 -0
- hatchit/templates/gitignore/gitignore.template +73 -0
- hatchit/templates/precommit/pre-commit-config.yaml +55 -0
- hatchit/templates/root/.coveragerc +13 -0
- hatchit/templates/root/.gitattributes +10 -0
- hatchit/templates/root/.readthedocs.yml +21 -0
- hatchit/templates/root/.zenodo.json +14 -0
- hatchit/templates/root/AUTHORS.md +3 -0
- hatchit/templates/root/CHANGELOG.md +11 -0
- hatchit/templates/root/CITATION.cff +10 -0
- hatchit/templates/root/CONTRIBUTING.md +20 -0
- hatchit/templates/root/LICENSE.md +21 -0
- hatchit/templates/tox/uv_tox.ini +58 -0
- hatchit/templates/uv_github_workflows/pre-commit.yml +16 -0
- hatchit/templates/uv_github_workflows/publish-pypi.yml +53 -0
- hatchit/templates/uv_github_workflows/run-tests.yml +72 -0
- hatchit-0.1.0.dist-info/METADATA +82 -0
- hatchit-0.1.0.dist-info/RECORD +38 -0
- hatchit-0.1.0.dist-info/WHEEL +4 -0
- hatchit-0.1.0.dist-info/entry_points.txt +2 -0
- hatchit-0.1.0.dist-info/licenses/AUTHORS.md +3 -0
- hatchit-0.1.0.dist-info/licenses/LICENSE.md +21 -0
hatchit/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from importlib.metadata import PackageNotFoundError, version # pragma: no cover
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
dist_name = __name__
|
|
6
|
+
__version__ = version(dist_name)
|
|
7
|
+
except PackageNotFoundError: # pragma: no cover
|
|
8
|
+
__version__ = "unknown"
|
|
9
|
+
finally:
|
|
10
|
+
del version, PackageNotFoundError
|
|
11
|
+
|
|
12
|
+
# exports
|
|
13
|
+
from .scaffold import create_hatchit_repository
|
hatchit/cli.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from .scaffold import create_hatchit_repository
|
|
6
|
+
|
|
7
|
+
__author__ = "Jayaram Kancherla"
|
|
8
|
+
__copyright__ = "Jayaram Kancherla"
|
|
9
|
+
__license__ = "MIT"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main():
|
|
13
|
+
parser = argparse.ArgumentParser(description="hatchit: Modern Python project scaffolder combining uv and hatch.")
|
|
14
|
+
parser.add_argument("project_path", help="Path where the new project should be created.")
|
|
15
|
+
parser.add_argument("--description", default="Add a short description here!", help="Optional project description.")
|
|
16
|
+
parser.add_argument("--license", default="MIT", help="License to use. Defaults to MIT.")
|
|
17
|
+
|
|
18
|
+
args = parser.parse_args()
|
|
19
|
+
|
|
20
|
+
project_path = args.project_path
|
|
21
|
+
|
|
22
|
+
if os.path.exists(project_path) and os.listdir(project_path):
|
|
23
|
+
print(f"Error: Directory {project_path} is not empty.")
|
|
24
|
+
sys.exit(1)
|
|
25
|
+
|
|
26
|
+
print(f"Hatching project at {project_path}...")
|
|
27
|
+
create_hatchit_repository(project_path=project_path, description=args.description, license=args.license)
|
|
28
|
+
print("Success! 🚀 💥")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
main()
|
hatchit/py.typed
ADDED
|
File without changes
|
hatchit/scaffold.py
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import shutil
|
|
3
|
+
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from warnings import warn
|
|
6
|
+
|
|
7
|
+
__author__ = "Jayaram Kancherla"
|
|
8
|
+
__copyright__ = "Jayaram Kancherla"
|
|
9
|
+
__license__ = "MIT"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_hatchit_repository(project_path: str, description: str, license: str) -> None:
|
|
13
|
+
"""Create a new Python project using uv and hatch.
|
|
14
|
+
|
|
15
|
+
This function initializes a new Python project at the specified `project_path`
|
|
16
|
+
using the `uv` tool and `hatch` for version management and project scaffolding.
|
|
17
|
+
It sets up a basic project structure, including source code, tests, documentation,
|
|
18
|
+
and configuration files. The function also customizes certain files based on the
|
|
19
|
+
provided `description` and `license`.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
project_path:
|
|
23
|
+
The path where the new project should be created.
|
|
24
|
+
|
|
25
|
+
description:
|
|
26
|
+
A short description of the project.
|
|
27
|
+
|
|
28
|
+
license:
|
|
29
|
+
The license to use for the project.
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
>>> create_hatchit_repository(
|
|
33
|
+
... "my_project",
|
|
34
|
+
... description="My new project",
|
|
35
|
+
... license="MIT",
|
|
36
|
+
... )
|
|
37
|
+
|
|
38
|
+
CLI usage:
|
|
39
|
+
$ hatchit my_project --description "My new project" --license MIT
|
|
40
|
+
"""
|
|
41
|
+
if description is None:
|
|
42
|
+
description = "Add a short description here!"
|
|
43
|
+
|
|
44
|
+
subprocess.run(["uv", "init", "--no-workspace", "--lib", project_path], check=True)
|
|
45
|
+
|
|
46
|
+
# Clear out the dummy hello function generated by uv init
|
|
47
|
+
init_path = Path(project_path) / "src" / project_path.split("/")[-1].replace("-", "_") / "__init__.py"
|
|
48
|
+
if init_path.exists():
|
|
49
|
+
with open(init_path, "w") as init_f:
|
|
50
|
+
init_content = """from importlib.metadata import PackageNotFoundError, version # pragma: no cover
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
dist_name = __name__
|
|
54
|
+
__version__ = version(dist_name)
|
|
55
|
+
except PackageNotFoundError: # pragma: no cover
|
|
56
|
+
__version__ = "unknown"
|
|
57
|
+
finally:
|
|
58
|
+
del version, PackageNotFoundError
|
|
59
|
+
"""
|
|
60
|
+
init_f.write(init_content)
|
|
61
|
+
|
|
62
|
+
modified_files = []
|
|
63
|
+
template_dir = Path(__file__).parent / "templates"
|
|
64
|
+
|
|
65
|
+
# Add GitHub Actions
|
|
66
|
+
gh_actions_dir = Path(project_path) / ".github" / "workflows"
|
|
67
|
+
gh_actions_dir.mkdir(parents=True, exist_ok=True)
|
|
68
|
+
|
|
69
|
+
for workflow in ["run-tests.yml", "publish-pypi.yml", "pre-commit.yml"]:
|
|
70
|
+
src = template_dir / "uv_github_workflows" / workflow
|
|
71
|
+
dst = gh_actions_dir / workflow
|
|
72
|
+
shutil.copy2(src, dst)
|
|
73
|
+
modified_files.append(dst)
|
|
74
|
+
|
|
75
|
+
# Add dependabot.yml
|
|
76
|
+
dependabot_src = template_dir / "github" / "dependabot.yml"
|
|
77
|
+
dependabot_dst = Path(project_path) / ".github" / "dependabot.yml"
|
|
78
|
+
shutil.copy2(dependabot_src, dependabot_dst)
|
|
79
|
+
modified_files.append(dependabot_dst)
|
|
80
|
+
|
|
81
|
+
# Add tox.ini
|
|
82
|
+
tox_src = template_dir / "tox" / "uv_tox.ini"
|
|
83
|
+
tox_dst = Path(project_path) / "tox.ini"
|
|
84
|
+
shutil.copy2(tox_src, tox_dst)
|
|
85
|
+
modified_files.append(tox_dst)
|
|
86
|
+
|
|
87
|
+
# Add comprehensive .gitignore
|
|
88
|
+
gitignore_src = template_dir / "gitignore" / "gitignore.template"
|
|
89
|
+
gitignore_dst = Path(project_path) / ".gitignore"
|
|
90
|
+
shutil.copy2(gitignore_src, gitignore_dst)
|
|
91
|
+
modified_files.append(gitignore_dst)
|
|
92
|
+
|
|
93
|
+
proj_name = Path(project_path).parts[-1]
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
git_name = subprocess.check_output(["git", "config", "user.name"], text=True).strip()
|
|
97
|
+
git_email = subprocess.check_output(["git", "config", "user.email"], text=True).strip()
|
|
98
|
+
except Exception as _:
|
|
99
|
+
warn("Git user.name and user.email not configured. Using default values.")
|
|
100
|
+
git_name = "First Author"
|
|
101
|
+
git_email = "first.author@example.com"
|
|
102
|
+
|
|
103
|
+
# Add top-level root files
|
|
104
|
+
for root_file in [
|
|
105
|
+
"CHANGELOG.md",
|
|
106
|
+
"CONTRIBUTING.md",
|
|
107
|
+
"LICENSE.md",
|
|
108
|
+
"AUTHORS.md",
|
|
109
|
+
".coveragerc",
|
|
110
|
+
".readthedocs.yml",
|
|
111
|
+
"CITATION.cff",
|
|
112
|
+
".gitattributes",
|
|
113
|
+
".zenodo.json",
|
|
114
|
+
]:
|
|
115
|
+
src = template_dir / "root" / root_file
|
|
116
|
+
dst = Path(project_path) / root_file
|
|
117
|
+
if src.exists():
|
|
118
|
+
if root_file == "LICENSE.md":
|
|
119
|
+
with open(src, "r") as f:
|
|
120
|
+
content = f.read()
|
|
121
|
+
|
|
122
|
+
with open(dst, "w") as f:
|
|
123
|
+
f.write(content.replace("REPLACE_PROJECT_NAME", proj_name))
|
|
124
|
+
elif root_file == "AUTHORS.md":
|
|
125
|
+
with open(src, "r") as f:
|
|
126
|
+
content = f.read()
|
|
127
|
+
|
|
128
|
+
with open(dst, "w") as f:
|
|
129
|
+
f.write(content.replace("First Author <first.author@example.com>", f"{git_name} <{git_email}>"))
|
|
130
|
+
elif root_file == "CITATION.cff":
|
|
131
|
+
name_parts = git_name.split(" ", 1)
|
|
132
|
+
given_names = name_parts[0]
|
|
133
|
+
family_names = name_parts[1] if len(name_parts) > 1 else ""
|
|
134
|
+
today_str = datetime.date.today().strftime("%Y-%m-%d")
|
|
135
|
+
|
|
136
|
+
with open(src, "r") as f:
|
|
137
|
+
content = f.read()
|
|
138
|
+
|
|
139
|
+
with open(dst, "w") as f:
|
|
140
|
+
f.write(
|
|
141
|
+
content
|
|
142
|
+
.replace("REPLACE_PROJECT_NAME", proj_name)
|
|
143
|
+
.replace("YOUR_FIRST_NAME", given_names)
|
|
144
|
+
.replace("YOUR_LAST_NAME", family_names)
|
|
145
|
+
.replace("REPLACE_DATE", today_str)
|
|
146
|
+
)
|
|
147
|
+
elif root_file == ".zenodo.json":
|
|
148
|
+
with open(src, "r") as f:
|
|
149
|
+
content = f.read()
|
|
150
|
+
|
|
151
|
+
with open(dst, "w") as f:
|
|
152
|
+
f.write(
|
|
153
|
+
content
|
|
154
|
+
.replace("REPLACE_PROJECT_NAME", proj_name)
|
|
155
|
+
.replace("REPLACE_DESCRIPTION", description)
|
|
156
|
+
.replace("YOUR_NAME", git_name)
|
|
157
|
+
)
|
|
158
|
+
else:
|
|
159
|
+
shutil.copy2(src, dst)
|
|
160
|
+
|
|
161
|
+
modified_files.append(dst)
|
|
162
|
+
|
|
163
|
+
# Add pre-commit config
|
|
164
|
+
precommit_src = template_dir / "precommit" / "pre-commit-config.yaml"
|
|
165
|
+
precommit_dst = Path(project_path) / ".pre-commit-config.yaml"
|
|
166
|
+
shutil.copy2(precommit_src, precommit_dst)
|
|
167
|
+
modified_files.append(precommit_dst)
|
|
168
|
+
|
|
169
|
+
# Modify README
|
|
170
|
+
readme_path = Path(project_path) / "README.md"
|
|
171
|
+
|
|
172
|
+
new_readme = f"""[](https://pypi.org/project/{proj_name}/)
|
|
173
|
+

|
|
174
|
+
|
|
175
|
+
# {proj_name}
|
|
176
|
+
|
|
177
|
+
> {description}
|
|
178
|
+
|
|
179
|
+
A longer description of your project goes here...
|
|
180
|
+
|
|
181
|
+
## Install
|
|
182
|
+
|
|
183
|
+
To get started, install the package from [PyPI](https://pypi.org/project/{proj_name}/)
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
pip install {proj_name}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
<!-- hatchit-notes -->
|
|
190
|
+
|
|
191
|
+
## Note
|
|
192
|
+
|
|
193
|
+
This project has been set up using [hatchit](https://github.com/biocpy/hatchit)
|
|
194
|
+
and [uv](https://docs.astral.sh/uv/).
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
with open(readme_path, "w") as f:
|
|
198
|
+
f.write(new_readme)
|
|
199
|
+
modified_files.append(readme_path)
|
|
200
|
+
|
|
201
|
+
# Add docs
|
|
202
|
+
docs_dir = Path(project_path) / "docs"
|
|
203
|
+
src_docs_dir = template_dir / "docs"
|
|
204
|
+
shutil.copytree(src_docs_dir, docs_dir, dirs_exist_ok=True)
|
|
205
|
+
|
|
206
|
+
pkg_name = proj_name.replace("-", "_")
|
|
207
|
+
|
|
208
|
+
conf_py_path = docs_dir / "conf.py"
|
|
209
|
+
if conf_py_path.exists():
|
|
210
|
+
with open(conf_py_path, "r") as f:
|
|
211
|
+
content = f.read()
|
|
212
|
+
|
|
213
|
+
with open(conf_py_path, "w") as f:
|
|
214
|
+
f.write(content.replace("REPLACE_PROJECT_NAME", pkg_name).replace("YOUR_NAME", git_name))
|
|
215
|
+
|
|
216
|
+
modified_files.append(conf_py_path)
|
|
217
|
+
|
|
218
|
+
index_md_path = docs_dir / "index.md"
|
|
219
|
+
if index_md_path.exists():
|
|
220
|
+
with open(index_md_path, "r") as f:
|
|
221
|
+
content = f.read()
|
|
222
|
+
|
|
223
|
+
with open(index_md_path, "w") as f:
|
|
224
|
+
f.write(content.replace("REPLACE_PROJECT_NAME", proj_name))
|
|
225
|
+
|
|
226
|
+
modified_files.append(index_md_path)
|
|
227
|
+
|
|
228
|
+
for f in docs_dir.glob("**/*"):
|
|
229
|
+
if f.is_file() and f not in modified_files:
|
|
230
|
+
modified_files.append(f)
|
|
231
|
+
|
|
232
|
+
# Modify pyproject.toml
|
|
233
|
+
pyprj_path = Path(project_path) / "pyproject.toml"
|
|
234
|
+
with open(pyprj_path, "r") as f:
|
|
235
|
+
pyprj_content = f.read()
|
|
236
|
+
|
|
237
|
+
ruff_config = """
|
|
238
|
+
[tool.ruff]
|
|
239
|
+
line-length = 120
|
|
240
|
+
src = ["src"]
|
|
241
|
+
exclude = ["tests", "docs"]
|
|
242
|
+
|
|
243
|
+
[tool.ruff.lint]
|
|
244
|
+
extend-ignore = ["F821"]
|
|
245
|
+
|
|
246
|
+
[tool.ruff.lint.pydocstyle]
|
|
247
|
+
convention = "google"
|
|
248
|
+
|
|
249
|
+
[tool.ruff.lint.per-file-ignores]
|
|
250
|
+
"__init__.py" = ["E402", "F401"]
|
|
251
|
+
|
|
252
|
+
[tool.ruff.format]
|
|
253
|
+
docstring-code-format = true
|
|
254
|
+
docstring-code-line-length = 20
|
|
255
|
+
|
|
256
|
+
[tool.mypy]
|
|
257
|
+
strict = true
|
|
258
|
+
|
|
259
|
+
[tool.pytest.ini_options]
|
|
260
|
+
addopts = "--cov --cov-report term-missing"
|
|
261
|
+
testpaths = ["tests"]
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
import re
|
|
265
|
+
|
|
266
|
+
# Remove hardcoded version and replace with dynamic versioning
|
|
267
|
+
pyprj_content = re.sub(r'version\s*=\s*"[^"]+"\n', 'dynamic = ["version"]\n', pyprj_content)
|
|
268
|
+
|
|
269
|
+
# Replace build-system with hatchling and hatch-vcs
|
|
270
|
+
build_system = """[build-system]
|
|
271
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
272
|
+
build-backend = "hatchling.build"
|
|
273
|
+
|
|
274
|
+
[tool.hatch.version]
|
|
275
|
+
source = "vcs"
|
|
276
|
+
fallback-version = "0.1.0"
|
|
277
|
+
"""
|
|
278
|
+
pyprj_content = re.sub(
|
|
279
|
+
r'\[build-system\].*?build-backend\s*=\s*"[^"]+"\n', build_system, pyprj_content, flags=re.DOTALL
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
pyprj_content = pyprj_content.replace('description = "Add your description here"', f'description = "{description}"')
|
|
283
|
+
|
|
284
|
+
with open(pyprj_path, "w") as f:
|
|
285
|
+
f.write(pyprj_content + ruff_config)
|
|
286
|
+
modified_files.append(pyprj_path)
|
|
287
|
+
|
|
288
|
+
# tests directory
|
|
289
|
+
tests_dir = Path(project_path) / "tests"
|
|
290
|
+
tests_dir.mkdir(parents=True, exist_ok=True)
|
|
291
|
+
test_file = tests_dir / "test_basic.py"
|
|
292
|
+
|
|
293
|
+
pkg_name = proj_name.replace("-", "_")
|
|
294
|
+
test_content = f"""import {pkg_name}
|
|
295
|
+
|
|
296
|
+
def test_import():
|
|
297
|
+
'''Test that the package can be imported.'''
|
|
298
|
+
assert {pkg_name}.__name__ == "{pkg_name}"
|
|
299
|
+
"""
|
|
300
|
+
with open(test_file, "w") as f:
|
|
301
|
+
f.write(test_content)
|
|
302
|
+
|
|
303
|
+
modified_files.append(test_file)
|
|
304
|
+
|
|
305
|
+
# Install dev dependencies
|
|
306
|
+
subprocess.run(
|
|
307
|
+
[
|
|
308
|
+
"uv",
|
|
309
|
+
"add",
|
|
310
|
+
"--dev",
|
|
311
|
+
"pytest",
|
|
312
|
+
"pytest-cov",
|
|
313
|
+
"sphinx",
|
|
314
|
+
"myst-nb",
|
|
315
|
+
"furo",
|
|
316
|
+
"sphinx-autodoc-typehints",
|
|
317
|
+
"linkify-it-py",
|
|
318
|
+
"mypy",
|
|
319
|
+
],
|
|
320
|
+
cwd=project_path,
|
|
321
|
+
check=True,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
try:
|
|
325
|
+
subprocess.run(["git", "init"], cwd=project_path, check=True, capture_output=True)
|
|
326
|
+
subprocess.run(["git", "add", "."], cwd=project_path, check=True, capture_output=True)
|
|
327
|
+
|
|
328
|
+
# Check if git user config exists before committing
|
|
329
|
+
name_configured = subprocess.run(["git", "config", "user.name"], capture_output=True).returncode == 0
|
|
330
|
+
email_configured = subprocess.run(["git", "config", "user.email"], capture_output=True).returncode == 0
|
|
331
|
+
|
|
332
|
+
if name_configured and email_configured:
|
|
333
|
+
subprocess.run(
|
|
334
|
+
["git", "commit", "-m", "chore: initial setup"], cwd=project_path, check=True, capture_output=True
|
|
335
|
+
)
|
|
336
|
+
except Exception:
|
|
337
|
+
warn("Git is not installed or not configured. Skipping git initialization and commit.")
|
|
338
|
+
pass
|
|
339
|
+
|
|
340
|
+
print("hatchit complete! 🚀 💥")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Makefile for Sphinx documentation
|
|
2
|
+
#
|
|
3
|
+
|
|
4
|
+
# You can set these variables from the command line, and also
|
|
5
|
+
# from the environment for the first two.
|
|
6
|
+
SPHINXOPTS ?=
|
|
7
|
+
SPHINXBUILD ?= sphinx-build
|
|
8
|
+
SOURCEDIR = .
|
|
9
|
+
BUILDDIR = _build
|
|
10
|
+
AUTODOCDIR = api
|
|
11
|
+
|
|
12
|
+
# User-friendly check for sphinx-build
|
|
13
|
+
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $?), 1)
|
|
14
|
+
$(error "The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://sphinx-doc.org/")
|
|
15
|
+
endif
|
|
16
|
+
|
|
17
|
+
.PHONY: help clean Makefile
|
|
18
|
+
|
|
19
|
+
# Put it first so that "make" without argument is like "make help".
|
|
20
|
+
help:
|
|
21
|
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
22
|
+
|
|
23
|
+
clean:
|
|
24
|
+
rm -rf $(BUILDDIR)/* $(AUTODOCDIR)
|
|
25
|
+
|
|
26
|
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
|
27
|
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
|
28
|
+
%: Makefile
|
|
29
|
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Empty directory
|