529 return absl::FailedPreconditionError(
530 "Project must load a hack manifest before message source sync");
532#if defined(__EMSCRIPTEN__)
534 return absl::FailedPreconditionError(
535 "message-source-sync --write is unavailable in browser builds because "
536 "durable atomic filesystem publication cannot be guaranteed");
540 if (!layout.
source.has_value()) {
541 return absl::FailedPreconditionError(
542 "Hack manifest does not define messages.source");
547 fs::path project_root;
550 fs::path canonical_bundle_path;
552 canonical_bundle_path,
553 ResolveProjectTarget(project_root, layout.
source->canonical_bundle_path,
554 true,
"Canonical message bundle"));
555 fs::path asm_include_path;
557 ResolveProjectTarget(
558 project_root, layout.
source->generated_asm_include_path,
559 false,
"Generated message ASM include"));
561 ValidateDistinctTargets(canonical_bundle_path, asm_include_path));
562 const std::vector<fs::path> publication_targets = {canonical_bundle_path,
565 publication_targets, kMessageSourcePublisherLabels));
567 std::error_code incoming_ec;
568 const fs::path resolved_incoming =
569 fs::canonical(incoming_bundle_path, incoming_ec);
570 if (incoming_ec || !fs::is_regular_file(resolved_incoming, incoming_ec) ||
572 return absl::NotFoundError(absl::StrFormat(
573 "Incoming message bundle must be an existing regular file: %s",
574 incoming_bundle_path.string()));
577 std::string expected_sha;
578 std::unique_ptr<core::SourceArtifactPublicationLock> write_locks;
581 return absl::InvalidArgumentError(
582 "--write requires --expected-source-sha256");
588 publication_targets, kMessageSourcePublisherLabels));
591 std::string canonical_before;
593 "canonical message bundle"));
594 const std::string source_sha_before =
596 Json canonical_document;
599 ParseBundleDocument(canonical_before,
"Canonical message bundle"));
600 ExpandedSourceBank merged_bank;
604 true,
"Canonical message bundle"));
605 const std::string expected_current_asm = RenderAsmInclude(
608 std::string incoming_content;
610 ReadTextFile(resolved_incoming,
"incoming message bundle"));
611 Json incoming_document;
614 ParseBundleDocument(incoming_content,
"Incoming message bundle"));
615 ExpandedSourceBank incoming_bank;
619 false,
"Incoming message bundle"));
620 for (
auto& [
id, message] : incoming_bank) {
621 merged_bank[id] = std::move(message);
624 size_t encoded_size = 1;
625 for (
const auto& entry : merged_bank) {
626 encoded_size += entry.second.bytes.size() + 1;
628 if (encoded_size > capacity) {
629 return absl::ResourceExhaustedError(absl::StrFormat(
630 "Expanded message source needs %zu bytes including terminators, but "
631 "manifest capacity is %zu bytes [0x%06X, 0x%06X]",
635 const std::string canonical_after = SerializeCanonicalBundle(merged_bank);
636 const std::string proposed_source_sha =
638 const std::string asm_after = RenderAsmInclude(
641 std::optional<std::string> asm_before;
642 std::error_code include_exists_ec;
643 if (fs::exists(asm_include_path, include_exists_ec)) {
644 std::string existing_include;
647 ReadTextFile(asm_include_path,
"generated message ASM include"));
648 asm_before = std::move(existing_include);
649 }
else if (include_exists_ec) {
650 return absl::FailedPreconditionError(
651 absl::StrFormat(
"Cannot inspect generated message ASM include: %s",
652 include_exists_ec.message()));
654 if (asm_before.has_value() && *asm_before != expected_current_asm) {
655 return absl::FailedPreconditionError(absl::StrFormat(
656 "Generated message ASM include drifted from the current canonical "
658 asm_include_path.string()));
663 .generated_asm_include_path = asm_include_path,
667 .incoming_updates =
static_cast<int>(incoming_bank.size()),
668 .encoded_size = encoded_size,
669 .capacity = capacity,
670 .source_sha256_before = source_sha_before,
671 .proposed_source_sha256 = proposed_source_sha,
672 .changed = canonical_before != canonical_after ||
673 !asm_before.has_value() || *asm_before != asm_after,
675 if (!options.
write) {
678 if (expected_sha != source_sha_before) {
679 return absl::AbortedError(absl::StrFormat(
680 "Canonical message source SHA-256 CAS failed: expected %s, got %s",
681 expected_sha, source_sha_before));
683 if (!result.changed) {
687 std::vector<core::SourceArtifactUpdate> updates;
689 .
target = canonical_bundle_path,
690 .before = canonical_before,
691 .after = canonical_after,
694 .
target = asm_include_path,
695 .before = asm_before,
699 *write_locks, std::move(updates), expected_sha, [&]() -> absl::Status {
702 std::string reopened_canonical;
704 ReadTextFile(canonical_bundle_path,
705 "published canonical message bundle"));
706 Json reopened_document;
709 ParseBundleDocument(reopened_canonical,
710 "Published canonical message bundle"));
711 ExpandedSourceBank reopened_bank;
716 "Published canonical message bundle"));
718 proposed_source_sha) {
719 return absl::DataLossError(
720 "Published canonical message bundle SHA-256 readback failed");
722 return absl::OkStatus();