Skip to content

Commit 1fc309a

Browse files
author
Martin Larralde
committed
Add documentation and --force to tomlgen_rust command
Signed-off-by: Martin Larralde <[email protected]>
1 parent 2f89418 commit 1fc309a

File tree

1 file changed

+92
-20
lines changed

1 file changed

+92
-20
lines changed

setuptools_rust/tomlgen.py

Lines changed: 92 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,23 @@
2323

2424
class tomlgen_rust(setuptools.Command):
2525

26-
user_options = [
26+
description = "Generate `Cargo.toml` for rust extensions"
2727

28+
user_options = [
29+
("create-workspace", 'w',
30+
"create a workspace file at the root of the project"),
31+
("force", 'f',
32+
"overwrite existing files if any")
2833
]
2934

35+
boolean_options = ['create_workspace', 'force']
36+
3037
def initialize_options(self):
3138

3239
self.dependencies = None
3340
self.authors = None
34-
self.create_workspace = False
35-
36-
# command to find build directories
37-
self.build = build(self.distribution)
41+
self.create_workspace = None
42+
self.force = None
3843

3944
# parse config files
4045
self.cfg = configparser.ConfigParser()
@@ -44,11 +49,10 @@ def finalize_options(self):
4449

4550
# Finalize previous commands
4651
self.distribution.finalize_options()
47-
self.build.ensure_finalized()
4852

4953
# Shortcuts
5054
self.extensions = self.distribution.rust_extensions
51-
self.workspace = os.path.dirname(self.distribution.script_name)
55+
self.workspace = os.path.abspath(os.path.dirname(self.distribution.script_name) or '.')
5256

5357
# Build list of authors
5458
if self.authors is not None:
@@ -57,41 +61,62 @@ def finalize_options(self):
5761
else:
5862
self.authors = '["{} <{}>"]'.format(
5963
self.distribution.get_author(),
60-
self.distribution.get_author_email())
64+
self.distribution.get_author_email().strip('"\''))
6165

6266
def run(self):
67+
68+
# Create a `Cargo.toml` for each extension
6369
for ext in self.extensions:
64-
log.info("creating 'Cargo.toml' for '%s'", ext.name)
6570
toml = self.build_cargo_toml(ext)
66-
with open(ext.path, 'w') as manifest:
67-
toml.write(manifest)
68-
71+
if not os.path.exists(ext.path) or self.force:
72+
log.info("creating 'Cargo.toml' for '%s'", ext.name)
73+
with open(ext.path, 'w') as manifest:
74+
toml.write(manifest)
75+
else:
76+
log.warn("skipping 'Cargo.toml' for '%s' -- already exists", ext.name)
77+
78+
# Create a `Cargo.toml` for the project workspace
6979
if self.create_workspace and self.extensions:
70-
log.info("creating 'Cargo.toml' for workspace")
7180
toml = self.build_workspace_toml()
72-
with open(os.path.join(self.workspace, "Cargo.toml"), 'w') as manifest:
73-
toml.write(manifest)
81+
if not os.path.exists(ext.path) or self.force:
82+
log.info("creating 'Cargo.toml' for workspace")
83+
toml_path = os.path.join(self.workspace, "Cargo.toml")
84+
with open(toml_path, 'w') as manifest:
85+
toml.write(manifest)
86+
else:
87+
log.warn("skipping 'Cargo.toml' for workspace -- already exists")
7488

7589
def build_cargo_toml(self, ext):
7690

91+
# Shortcuts
7792
quote = '"{}"'.format
7893
dist = self.distribution
94+
95+
# Use a ConfigParser object to build a TOML file (hackish)
7996
toml = configparser.ConfigParser()
8097

98+
# The directory where the extension's manifest is located
99+
tomldir = os.path.dirname(ext.path)
100+
101+
# Create a small package section
81102
toml.add_section("package")
82103
toml.set("package", "name", quote(ext.name))
83104
toml.set("package", "version", quote(dist.get_version()))
84105
toml.set("package", "authors", self.authors)
85106
toml.set("package", "publish", "false")
86107

108+
# Add the relative path to the workspace if any
87109
if self.create_workspace:
88-
toml.set("package", "workspace", quote(os.path.abspath(self.workspace)))
110+
path_to_workspace = os.path.relpath(self.workspace, tomldir)
111+
toml.set("package", "workspace", quote(path_to_workspace))
89112

113+
# Create a small lib section
90114
toml.add_section("lib")
91115
toml.set("lib", "crate-type", '["cdylib"]')
92-
toml.set("lib", "name", quote(ext.basename))
93-
toml.set("lib", "path", quote(ext.libfile))
116+
toml.set("lib", "name", quote(_slugify(ext.name)))
117+
toml.set("lib", "path", quote(os.path.relpath(ext.libfile, tomldir)))
94118

119+
# Find dependencies within the `setup.cfg` file of the project
95120
toml.add_section("dependencies")
96121
for dep, options in self.iter_dependencies(ext):
97122
toml.set("dependencies", dep, options)
@@ -100,9 +125,11 @@ def build_cargo_toml(self, ext):
100125

101126
def build_workspace_toml(self):
102127

128+
# Find all members of the workspace
103129
members = [os.path.dirname(os.path.relpath(ext.path)) for ext in self.extensions]
104130
members = ['"{}"'.format(m) for m in members]
105131

132+
# Create the `Cargo.toml` content using a ConfigParser
106133
toml = configparser.ConfigParser()
107134
toml.add_section('workspace')
108135
toml.set('workspace', 'members', '[{}]'.format(', '.join(members)))
@@ -123,13 +150,59 @@ def iter_dependencies(self, ext=None):
123150
yield dep, options
124151

125152

126-
127153
def _slugify(name):
128154
allowed = set(string.ascii_letters + string.digits + '_')
129155
slug = [char if char in allowed else '_' for char in name]
130156
return ''.join(slug)
131157

132158
def find_rust_extensions(*directories, libfile="lib.rs", **kwargs):
159+
"""Attempt to find Rust extensions in given directories.
160+
161+
This function will recurse through the directories in the given
162+
directories, to find a name whose name is ``libfile``. When such
163+
a file is found, an extension is created, expecting the cargo
164+
manifest file (``Cargo.toml``) to be next to that file. The
165+
extension destination will be deduced from the name of the
166+
directory where that ``libfile`` is contained.
167+
168+
Arguments:
169+
directories (list, *optional*): a list of directories to walk
170+
through recursively to find extensions. If none are given,
171+
then the current directory will be used instead.
172+
173+
Keyword Arguments:
174+
libfile (str): the name of the file to look for when searching
175+
for Rust extensions. Defaults to ``lib.rs``, but might be
176+
changed to allow defining more *Pythonic* filenames
177+
(like ``__init__.rs``)!
178+
179+
Note:
180+
All other keyword arguments will be directly passed to the
181+
`RustExtension` instance created when an extension is found.
182+
One may be interested in passing ``bindings`` and ``strip``
183+
options.
184+
185+
Example:
186+
187+
Consider the following project::
188+
189+
lib/
190+
└ mylib/
191+
└ rustext/
192+
├ lib.rs
193+
├ ...
194+
└ Cargo.toml
195+
setup.py
196+
197+
The only extension can be found in the ``lib`` module:
198+
199+
.. code-block:: python
200+
201+
>>> import setuptools_rust as rust
202+
>>> for ext in rust.find_rust_extensions("lib"):
203+
... print(ext.name, "=>", ext.path)
204+
lib.mylib.rustext => lib/mylib/rustext/Cargo.toml
205+
"""
133206

134207
directories = directories or [os.getcwd()]
135208
extensions = []
@@ -141,7 +214,6 @@ def find_rust_extensions(*directories, libfile="lib.rs", **kwargs):
141214
tomlpath = os.path.join(base, "Cargo.toml")
142215
ext = RustExtension(dotpath, tomlpath, **kwargs)
143216
ext.libfile = os.path.join(base, libfile)
144-
ext.basename = os.path.basename(base)
145217
extensions.append(ext)
146218

147219
return extensions

0 commit comments

Comments
 (0)