@@ -5,7 +5,7 @@ use std::{
5
5
env,
6
6
ffi:: OsString ,
7
7
fs,
8
- io:: Write ,
8
+ io:: { Cursor , Write } ,
9
9
os:: unix:: fs:: PermissionsExt ,
10
10
path:: { Path , PathBuf } ,
11
11
process:: Command ,
@@ -18,6 +18,8 @@ use log::{debug, info};
18
18
use semver:: Version ;
19
19
use serde_json:: Value ;
20
20
21
+ use tar:: { Builder , Archive } ;
22
+
21
23
use crate :: files:: {
22
24
cgi_conf:: CgiConf , manifest:: Manifest , package_conf:: PackageConf , param_conf:: ParamConf ,
23
25
} ;
@@ -27,65 +29,6 @@ mod json_ext;
27
29
28
30
mod files;
29
31
30
- // TODO: Find a better way to support reproducible builds
31
- fn copy < P : AsRef < Path > , Q : AsRef < Path > > (
32
- src : P ,
33
- dst : Q ,
34
- copy_permissions : bool ,
35
- ) -> anyhow:: Result < ( ) > {
36
- let src = src. as_ref ( ) ;
37
- let dst = dst. as_ref ( ) ;
38
- if dst. symlink_metadata ( ) . is_ok ( ) {
39
- bail ! ( "Path already exists {dst:?}" ) ;
40
- }
41
- if src. is_symlink ( ) {
42
- // FIXME: Copy symlink in Rust
43
- let mut cp = Command :: new ( "cp" ) ;
44
-
45
- if copy_permissions {
46
- cp. arg ( "--preserve=mode" ) ;
47
- }
48
-
49
- cp. arg ( "-dn" ) . arg ( src. as_os_str ( ) ) . arg ( dst. as_os_str ( ) ) ;
50
-
51
- if !cp. status ( ) ?. success ( ) {
52
- bail ! ( "Failed to copy symlink: {}" , src. display( ) ) ;
53
- }
54
- } else if copy_permissions {
55
- fs:: copy ( src, dst) ?;
56
- } else {
57
- let mut src = fs:: File :: open ( src) ?;
58
- let mut dst = fs:: File :: create ( dst) ?;
59
- std:: io:: copy ( & mut src, & mut dst) ?;
60
- }
61
- Ok ( ( ) )
62
- }
63
-
64
- fn copy_recursively ( src : & Path , dst : & Path , copy_permissions : bool ) -> anyhow:: Result < ( ) > {
65
- if !src. is_dir ( ) {
66
- copy ( src, dst, copy_permissions) ?;
67
- debug ! ( "Created reg {dst:?}" ) ;
68
- return Ok ( ( ) ) ;
69
- }
70
- match fs:: create_dir ( dst) {
71
- Ok ( ( ) ) => {
72
- debug ! ( "Created dir {dst:?}" ) ;
73
- Ok ( ( ) )
74
- }
75
- Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: AlreadyExists => Ok ( ( ) ) ,
76
- Err ( e) => Err ( e) ,
77
- } ?;
78
- for entry in fs:: read_dir ( src) ? {
79
- let entry = entry?;
80
- copy_recursively (
81
- & entry. path ( ) ,
82
- & dst. join ( entry. file_name ( ) ) ,
83
- copy_permissions,
84
- ) ?;
85
- }
86
- Ok ( ( ) )
87
- }
88
-
89
32
enum AcapBuildImpl {
90
33
Reference ,
91
34
Equivalent ,
@@ -111,6 +54,7 @@ pub struct AppBuilder<'a> {
111
54
files : Vec < String > ,
112
55
default_architecture : Architecture ,
113
56
app_name : String ,
57
+ ar: Option <Builder :: <Cursor :: <Vec :: < u8 > >>>,
114
58
}
115
59
116
60
impl <' a > AppBuilder < ' a > {
@@ -123,13 +67,16 @@ impl<'a> AppBuilder<'a> {
123
67
let manifest: Value = serde_json:: from_reader ( fs:: File :: open ( manifest) ?) ?;
124
68
let manifest = Manifest :: new ( manifest, default_architecture) ?;
125
69
let app_name = manifest. try_find_app_name ( ) ?. to_string ( ) ;
70
+ let mut ar = Builder :: new ( Cursor :: new ( Vec :: new ( ) ) ) ;
71
+ ar. follow_symlinks ( false ) ;
126
72
Ok ( Self {
127
73
preserve_permissions,
128
74
staging_dir,
129
75
manifest,
130
76
app_name,
131
77
files : Vec :: new ( ) ,
132
78
default_architecture,
79
+ ar : Some ( ar) ,
133
80
} )
134
81
}
135
82
@@ -161,20 +108,15 @@ impl<'a> AppBuilder<'a> {
161
108
Ok ( self )
162
109
}
163
110
164
- // TODO: Remove the file system copy
165
111
pub fn add_as ( & mut self , path : & Path , name : & str ) -> anyhow:: Result < PathBuf > {
166
- let dst = self . staging_dir . join ( name) ;
167
- if dst. symlink_metadata ( ) . is_ok ( ) {
168
- bail ! ( "Cannot add {path:?} because {name} already exists" ) ;
169
- }
170
- copy_recursively ( path, & dst, self . preserve_permissions ) ?;
171
- self . files . push ( name. to_string ( ) ) ;
172
- if name == self . app_name && !self . preserve_permissions {
173
- let mut permissions = fs:: metadata ( & dst) ?. permissions ( ) ;
174
- let mode = permissions. mode ( ) ;
175
- permissions. set_mode ( mode | 0o111 ) ;
176
- fs:: set_permissions ( & dst, permissions) ?;
112
+ if path. symlink_metadata ( ) . unwrap ( ) . file_type ( ) . is_dir ( ) {
113
+ self . ar . as_mut ( ) . unwrap ( ) . append_dir_all ( name, path) ?;
114
+ } else {
115
+ self . ar . as_mut ( ) . unwrap ( ) . append_path_with_name ( path, name) ?;
177
116
}
117
+
118
+ let dst = self . staging_dir . join ( name) ;
119
+
178
120
debug ! ( "Added {name} from {path:?}" ) ;
179
121
Ok ( dst)
180
122
}
@@ -188,7 +130,13 @@ impl<'a> AppBuilder<'a> {
188
130
}
189
131
190
132
/// Build the EAP and return its path.
191
- pub fn build ( self ) -> anyhow:: Result < OsString > {
133
+ pub fn build ( mut self ) -> anyhow:: Result < OsString > {
134
+ let cursor = Cursor :: new ( self . ar . take ( ) . unwrap ( ) . into_inner ( ) ?. into_inner ( ) ) ;
135
+ let mut archive = Archive :: new ( cursor) ;
136
+ archive. set_preserve_permissions ( self . preserve_permissions ) ;
137
+ archive. set_preserve_mtime ( false ) ;
138
+ archive. unpack ( self . staging_dir ) ?;
139
+
192
140
match AcapBuildImpl :: from_env_or_default ( ) ? {
193
141
AcapBuildImpl :: Reference => {
194
142
debug ! ( "Using acap-build" ) ;
@@ -206,14 +154,9 @@ impl<'a> AppBuilder<'a> {
206
154
let Self {
207
155
staging_dir,
208
156
default_architecture,
209
- manifest,
210
157
..
211
158
} = & self ;
212
159
213
- fs:: File :: create_new ( staging_dir. join ( "manifest.json" ) )
214
- . context ( "creating manifest.json" ) ?
215
- . write_all ( manifest. try_to_string ( ) ?. as_bytes ( ) ) ?;
216
-
217
160
let mut acap_build = Command :: new ( "acap-build" ) ;
218
161
acap_build. args ( [ "--build" , "no-build" ] ) ;
219
162
for file in self . additional_files ( ) {
@@ -424,6 +367,7 @@ impl<'a> AppBuilder<'a> {
424
367
[
425
368
Some ( self . app_name . as_str ( ) ) ,
426
369
Some ( "LICENSE" ) ,
370
+ Some ( "manifest.json" ) ,
427
371
self . manifest . try_find_post_install_script ( ) . ok ( ) ,
428
372
self . manifest . try_find_pre_uninstall_script ( ) . ok ( ) ,
429
373
]
0 commit comments