Skip to content

Commit 9328b12

Browse files
committed
cargo publish: Revert back to extracting tar file.
This constructs the new Cargo.lock in memory instead of writing it directly to disk. The in-memory copy is added to the tar file directly.
1 parent 6eb55ab commit 9328b12

File tree

4 files changed

+101
-82
lines changed

4 files changed

+101
-82
lines changed

src/cargo/ops/cargo_package.rs

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ use std::fs::{self, File};
33
use std::io::prelude::*;
44
use std::io::SeekFrom;
55
use std::path::{self, Path, PathBuf};
6+
use std::rc::Rc;
67
use std::sync::Arc;
78

9+
use flate2::read::GzDecoder;
810
use flate2::{Compression, GzBuilder};
911
use log::debug;
1012
use serde_json::{self, json};
11-
use tar::{Builder, EntryType, Header};
13+
use tar::{Archive, Builder, EntryType, Header};
1214
use termcolor::Color;
1315

1416
use crate::core::compiler::{BuildConfig, CompileMode, DefaultExecutor, Executor};
@@ -20,6 +22,7 @@ use crate::ops;
2022
use crate::sources::PathSource;
2123
use crate::util::errors::{CargoResult, CargoResultExt};
2224
use crate::util::paths;
25+
use crate::util::toml::TomlManifest;
2326
use crate::util::{self, internal, Config, FileLock};
2427

2528
pub struct PackageOpts<'cfg> {
@@ -102,7 +105,7 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option
102105
.shell()
103106
.status("Packaging", pkg.package_id().to_string())?;
104107
dst.file().set_len(0)?;
105-
tar(ws, &src_files, vcs_info.as_ref(), &dst, &filename)
108+
tar(ws, &src_files, vcs_info.as_ref(), dst.file(), &filename)
106109
.chain_err(|| failure::format_err!("failed to prepare local package for uploading"))?;
107110
if opts.verify {
108111
dst.seek(SeekFrom::Start(0))?;
@@ -118,6 +121,34 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option
118121
Ok(Some(dst))
119122
}
120123

124+
/// Construct `Cargo.lock` for the package to be published.
125+
fn build_lock(ws: &Workspace<'_>) -> CargoResult<String> {
126+
let config = ws.config();
127+
let orig_resolve = ops::load_pkg_lockfile(ws)?;
128+
129+
// Convert Package -> TomlManifest -> Manifest -> Package
130+
let orig_pkg = ws.current()?;
131+
let toml_manifest = Rc::new(orig_pkg.manifest().original().prepare_for_publish(config)?);
132+
let package_root = orig_pkg.root();
133+
let source_id = orig_pkg.package_id().source_id();
134+
let (manifest, _nested_paths) =
135+
TomlManifest::to_real_manifest(&toml_manifest, source_id, package_root, config)?;
136+
let new_pkg = Package::new(manifest, orig_pkg.manifest_path());
137+
138+
// Regenerate Cargo.lock using the old one as a guide.
139+
let specs = vec![PackageIdSpec::from_package_id(new_pkg.package_id())];
140+
let tmp_ws = Workspace::ephemeral(new_pkg, ws.config(), None, true)?;
141+
let (pkg_set, new_resolve) =
142+
ops::resolve_ws_with_method(&tmp_ws, None, Method::Everything, &specs)?;
143+
144+
if let Some(orig_resolve) = orig_resolve {
145+
compare_resolve(config, tmp_ws.current()?, &orig_resolve, &new_resolve)?;
146+
}
147+
check_yanked(config, &pkg_set, &new_resolve)?;
148+
149+
ops::resolve_to_string(&tmp_ws, &new_resolve)
150+
}
151+
121152
fn include_lockfile(pkg: &Package) -> bool {
122153
pkg.manifest().publish_lockfile() && pkg.targets().iter().any(|t| t.is_example() || t.is_bin())
123154
}
@@ -283,28 +314,21 @@ fn tar(
283314
ws: &Workspace<'_>,
284315
src_files: &[PathBuf],
285316
vcs_info: Option<&serde_json::Value>,
286-
dst: &FileLock,
317+
dst: &File,
287318
filename: &str,
288319
) -> CargoResult<()> {
289320
// Prepare the encoder and its header.
290321
let filename = Path::new(filename);
291322
let encoder = GzBuilder::new()
292323
.filename(util::path2bytes(filename)?)
293-
.write(dst.file(), Compression::best());
324+
.write(dst, Compression::best());
294325

295326
// Put all package files into a compressed archive.
296327
let mut ar = Builder::new(encoder);
297328
let pkg = ws.current()?;
298329
let config = ws.config();
299330
let root = pkg.root();
300-
// While creating the tar file, also copy to the output directory.
301-
let dest_copy_root = dst
302-
.parent()
303-
.join(format!("{}-{}", pkg.name(), pkg.version()));
304-
if dest_copy_root.exists() {
305-
paths::remove_dir_all(&dest_copy_root)?;
306-
}
307-
fs::create_dir_all(&dest_copy_root)?;
331+
308332
for src_file in src_files {
309333
let relative = src_file.strip_prefix(root)?;
310334
check_filename(relative)?;
@@ -369,19 +393,11 @@ fn tar(
369393
ar.append(&header, toml.as_bytes()).chain_err(|| {
370394
internal(format!("could not archive source file `{}`", relative_str))
371395
})?;
372-
fs::write(dest_copy_root.join(relative), toml)?;
373-
fs::copy(src_file, dest_copy_root.join("Cargo.toml.orig"))?;
374396
} else {
375397
header.set_cksum();
376398
ar.append(&header, &mut file).chain_err(|| {
377399
internal(format!("could not archive source file `{}`", relative_str))
378400
})?;
379-
let dest = dest_copy_root.join(relative);
380-
let parent = dest.parent().unwrap();
381-
if !parent.exists() {
382-
fs::create_dir_all(parent)?;
383-
}
384-
fs::copy(src_file, dest)?;
385401
}
386402
}
387403

@@ -415,29 +431,8 @@ fn tar(
415431
}
416432

417433
if include_lockfile(pkg) {
418-
let orig_lock_path = ws.root().join("Cargo.lock");
419-
let new_lock_path = dest_copy_root.join("Cargo.lock");
420-
if orig_lock_path.exists() {
421-
fs::copy(&orig_lock_path, &new_lock_path)?;
422-
}
434+
let new_lock = build_lock(&ws)?;
423435

424-
// Regenerate Cargo.lock using the old one as a guide.
425-
let orig_resolve = ops::load_pkg_lockfile(ws)?;
426-
let id = SourceId::for_path(&dest_copy_root)?;
427-
let mut src = PathSource::new(&dest_copy_root, id, ws.config());
428-
let new_pkg = src.root_package()?;
429-
let specs = vec![PackageIdSpec::from_package_id(new_pkg.package_id())];
430-
let tmp_ws = Workspace::ephemeral(new_pkg, config, None, true)?;
431-
let (pkg_set, new_resolve) =
432-
ops::resolve_ws_with_method(&tmp_ws, None, Method::Everything, &specs)?;
433-
// resolve_ws_with_method won't save for ephemeral, do it manually.
434-
ops::write_pkg_lockfile(&tmp_ws, &new_resolve)?;
435-
if let Some(orig_resolve) = orig_resolve {
436-
compare_resolve(config, tmp_ws.current()?, &orig_resolve, &new_resolve)?;
437-
}
438-
check_yanked(config, &pkg_set, &new_resolve)?;
439-
440-
let toml = paths::read(&new_lock_path)?;
441436
let path = format!(
442437
"{}-{}{}Cargo.lock",
443438
pkg.name(),
@@ -448,9 +443,9 @@ fn tar(
448443
header.set_path(&path)?;
449444
header.set_entry_type(EntryType::file());
450445
header.set_mode(0o644);
451-
header.set_size(toml.len() as u64);
446+
header.set_size(new_lock.len() as u64);
452447
header.set_cksum();
453-
ar.append(&header, toml.as_bytes())
448+
ar.append(&header, new_lock.as_bytes())
454449
.chain_err(|| internal("could not archive source file `Cargo.lock`"))?;
455450
}
456451

@@ -566,9 +561,18 @@ fn run_verify(ws: &Workspace<'_>, tar: &FileLock, opts: &PackageOpts<'_>) -> Car
566561

567562
config.shell().status("Verifying", pkg)?;
568563

564+
let f = GzDecoder::new(tar.file());
569565
let dst = tar
570566
.parent()
571567
.join(&format!("{}-{}", pkg.name(), pkg.version()));
568+
if dst.exists() {
569+
paths::remove_dir_all(&dst)?;
570+
}
571+
let mut archive = Archive::new(f);
572+
// We don't need to set the Modified Time, as it's not relevant to verification
573+
// and it errors on filesystems that don't support setting a modified timestamp
574+
archive.set_preserve_mtime(false);
575+
archive.unpack(dst.parent().unwrap())?;
572576

573577
// Manufacture an ephemeral workspace to ensure that even if the top-level
574578
// package has a workspace we can still build our new crate.

src/cargo/ops/lockfile.rs

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,57 @@ pub fn load_pkg_lockfile(ws: &Workspace<'_>) -> CargoResult<Option<Resolve>> {
2929
Ok(resolve)
3030
}
3131

32+
/// Generate a toml String of Cargo.lock from a Resolve.
33+
pub fn resolve_to_string(ws: &Workspace<'_>, resolve: &Resolve) -> CargoResult<String> {
34+
let (_orig, out, _ws_root) = resolve_to_string_orig(ws, resolve)?;
35+
Ok(out)
36+
}
37+
3238
pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &Resolve) -> CargoResult<()> {
39+
let (orig, out, ws_root) = resolve_to_string_orig(ws, resolve)?;
40+
41+
// If the lock file contents haven't changed so don't rewrite it. This is
42+
// helpful on read-only filesystems.
43+
if let Some(orig) = orig {
44+
if are_equal_lockfiles(orig, &out, ws) {
45+
return Ok(());
46+
}
47+
}
48+
49+
if !ws.config().lock_update_allowed() {
50+
if ws.config().cli_unstable().offline {
51+
failure::bail!("can't update in the offline mode");
52+
}
53+
54+
let flag = if ws.config().network_allowed() {
55+
"--locked"
56+
} else {
57+
"--frozen"
58+
};
59+
failure::bail!(
60+
"the lock file {} needs to be updated but {} was passed to \
61+
prevent this",
62+
ws.root().to_path_buf().join("Cargo.lock").display(),
63+
flag
64+
);
65+
}
66+
67+
// Ok, if that didn't work just write it out
68+
ws_root
69+
.open_rw("Cargo.lock", ws.config(), "Cargo.lock file")
70+
.and_then(|mut f| {
71+
f.file().set_len(0)?;
72+
f.write_all(out.as_bytes())?;
73+
Ok(())
74+
})
75+
.chain_err(|| format!("failed to write {}", ws.root().join("Cargo.lock").display()))?;
76+
Ok(())
77+
}
78+
79+
fn resolve_to_string_orig(
80+
ws: &Workspace<'_>,
81+
resolve: &Resolve,
82+
) -> CargoResult<(Option<String>, String, Filesystem)> {
3383
// Load the original lock file if it exists.
3484
let ws_root = Filesystem::new(ws.root().to_path_buf());
3585
let orig = ws_root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file");
@@ -94,42 +144,7 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &Resolve) -> CargoResult<
94144
out.push_str(&meta.to_string());
95145
}
96146

97-
// If the lock file contents haven't changed so don't rewrite it. This is
98-
// helpful on read-only filesystems.
99-
if let Ok(orig) = orig {
100-
if are_equal_lockfiles(orig, &out, ws) {
101-
return Ok(());
102-
}
103-
}
104-
105-
if !ws.config().lock_update_allowed() {
106-
if ws.config().cli_unstable().offline {
107-
failure::bail!("can't update in the offline mode");
108-
}
109-
110-
let flag = if ws.config().network_allowed() {
111-
"--locked"
112-
} else {
113-
"--frozen"
114-
};
115-
failure::bail!(
116-
"the lock file {} needs to be updated but {} was passed to \
117-
prevent this",
118-
ws.root().to_path_buf().join("Cargo.lock").display(),
119-
flag
120-
);
121-
}
122-
123-
// Ok, if that didn't work just write it out
124-
ws_root
125-
.open_rw("Cargo.lock", ws.config(), "Cargo.lock file")
126-
.and_then(|mut f| {
127-
f.file().set_len(0)?;
128-
f.write_all(out.as_bytes())?;
129-
Ok(())
130-
})
131-
.chain_err(|| format!("failed to write {}", ws.root().join("Cargo.lock").display()))?;
132-
Ok(())
147+
Ok((orig.ok(), out, ws_root))
133148
}
134149

135150
fn are_equal_lockfiles(mut orig: String, current: &str, ws: &Workspace<'_>) -> bool {

src/cargo/ops/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub use self::cargo_run::run;
1616
pub use self::cargo_test::{run_benches, run_tests, TestOptions};
1717
pub use self::cargo_uninstall::uninstall;
1818
pub use self::fix::{fix, fix_maybe_exec_rustc, FixOptions};
19-
pub use self::lockfile::{load_pkg_lockfile, write_pkg_lockfile};
19+
pub use self::lockfile::{load_pkg_lockfile, resolve_to_string, write_pkg_lockfile};
2020
pub use self::registry::HttpTimeout;
2121
pub use self::registry::{configure_http_handle, http_handle_and_timeout};
2222
pub use self::registry::{http_handle, needs_custom_http_transport, registry_login, search};

src/cargo/util/toml/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,7 @@ impl TomlManifest {
822822
}
823823
}
824824

825-
fn to_real_manifest(
825+
pub fn to_real_manifest(
826826
me: &Rc<TomlManifest>,
827827
source_id: SourceId,
828828
package_root: &Path,

0 commit comments

Comments
 (0)