23
23
24
24
class tomlgen_rust (setuptools .Command ):
25
25
26
- user_options = [
26
+ description = "Generate `Cargo.toml` for rust extensions"
27
27
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" )
28
33
]
29
34
35
+ boolean_options = ['create_workspace' , 'force' ]
36
+
30
37
def initialize_options (self ):
31
38
32
39
self .dependencies = None
33
40
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
38
43
39
44
# parse config files
40
45
self .cfg = configparser .ConfigParser ()
@@ -44,11 +49,10 @@ def finalize_options(self):
44
49
45
50
# Finalize previous commands
46
51
self .distribution .finalize_options ()
47
- self .build .ensure_finalized ()
48
52
49
53
# Shortcuts
50
54
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 '.' )
52
56
53
57
# Build list of authors
54
58
if self .authors is not None :
@@ -57,41 +61,62 @@ def finalize_options(self):
57
61
else :
58
62
self .authors = '["{} <{}>"]' .format (
59
63
self .distribution .get_author (),
60
- self .distribution .get_author_email ())
64
+ self .distribution .get_author_email (). strip ( '" \' ' ) )
61
65
62
66
def run (self ):
67
+
68
+ # Create a `Cargo.toml` for each extension
63
69
for ext in self .extensions :
64
- log .info ("creating 'Cargo.toml' for '%s'" , ext .name )
65
70
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
69
79
if self .create_workspace and self .extensions :
70
- log .info ("creating 'Cargo.toml' for workspace" )
71
80
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" )
74
88
75
89
def build_cargo_toml (self , ext ):
76
90
91
+ # Shortcuts
77
92
quote = '"{}"' .format
78
93
dist = self .distribution
94
+
95
+ # Use a ConfigParser object to build a TOML file (hackish)
79
96
toml = configparser .ConfigParser ()
80
97
98
+ # The directory where the extension's manifest is located
99
+ tomldir = os .path .dirname (ext .path )
100
+
101
+ # Create a small package section
81
102
toml .add_section ("package" )
82
103
toml .set ("package" , "name" , quote (ext .name ))
83
104
toml .set ("package" , "version" , quote (dist .get_version ()))
84
105
toml .set ("package" , "authors" , self .authors )
85
106
toml .set ("package" , "publish" , "false" )
86
107
108
+ # Add the relative path to the workspace if any
87
109
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 ))
89
112
113
+ # Create a small lib section
90
114
toml .add_section ("lib" )
91
115
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 ) ))
94
118
119
+ # Find dependencies within the `setup.cfg` file of the project
95
120
toml .add_section ("dependencies" )
96
121
for dep , options in self .iter_dependencies (ext ):
97
122
toml .set ("dependencies" , dep , options )
@@ -100,9 +125,11 @@ def build_cargo_toml(self, ext):
100
125
101
126
def build_workspace_toml (self ):
102
127
128
+ # Find all members of the workspace
103
129
members = [os .path .dirname (os .path .relpath (ext .path )) for ext in self .extensions ]
104
130
members = ['"{}"' .format (m ) for m in members ]
105
131
132
+ # Create the `Cargo.toml` content using a ConfigParser
106
133
toml = configparser .ConfigParser ()
107
134
toml .add_section ('workspace' )
108
135
toml .set ('workspace' , 'members' , '[{}]' .format (', ' .join (members )))
@@ -123,13 +150,59 @@ def iter_dependencies(self, ext=None):
123
150
yield dep , options
124
151
125
152
126
-
127
153
def _slugify (name ):
128
154
allowed = set (string .ascii_letters + string .digits + '_' )
129
155
slug = [char if char in allowed else '_' for char in name ]
130
156
return '' .join (slug )
131
157
132
158
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
+ """
133
206
134
207
directories = directories or [os .getcwd ()]
135
208
extensions = []
@@ -141,7 +214,6 @@ def find_rust_extensions(*directories, libfile="lib.rs", **kwargs):
141
214
tomlpath = os .path .join (base , "Cargo.toml" )
142
215
ext = RustExtension (dotpath , tomlpath , ** kwargs )
143
216
ext .libfile = os .path .join (base , libfile )
144
- ext .basename = os .path .basename (base )
145
217
extensions .append (ext )
146
218
147
219
return extensions
0 commit comments