7zip-native 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/native/addon.cpp CHANGED
@@ -486,6 +486,11 @@ struct Entry {
486
486
  bool encrypted = false;
487
487
  };
488
488
 
489
+ struct ArchiveCreateItem {
490
+ std::string sourcePath;
491
+ std::string path;
492
+ };
493
+
489
494
  std::uint64_t GetPropUInt64(IInArchive *archive, std::uint32_t index, PROPID propId)
490
495
  {
491
496
  NWindows::NCOM::CPropVariant value;
@@ -631,6 +636,152 @@ Z7_COM7F_IMF(FileInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosi
631
636
  return S_OK;
632
637
  }
633
638
 
639
+ class CreateFileInStream final : public ISequentialInStream, public CMyUnknownImp {
640
+ Z7_COM_UNKNOWN_IMP_1(ISequentialInStream)
641
+ Z7_COM7F_IMP(Read(void *data, UInt32 size, UInt32 *processedSize))
642
+
643
+ public:
644
+ bool Open(const std::string& path)
645
+ {
646
+ file_.open(NativePath(path), std::ios::binary);
647
+ return file_.is_open();
648
+ }
649
+
650
+ private:
651
+ std::ifstream file_;
652
+ };
653
+
654
+ Z7_COM7F_IMF(CreateFileInStream::Read(void *data, UInt32 size, UInt32 *processedSize))
655
+ {
656
+ file_.read(static_cast<char *>(data), static_cast<std::streamsize>(size));
657
+ const auto count = file_.gcount();
658
+ if (processedSize != nullptr) {
659
+ *processedSize = static_cast<UInt32>(count);
660
+ }
661
+ return file_.bad() ? E_FAIL : S_OK;
662
+ }
663
+
664
+ class CreateFileOutStream final : public IOutStream, public CMyUnknownImp {
665
+ Z7_COM_QI_BEGIN2(IOutStream)
666
+ Z7_COM_QI_ENTRY(ISequentialOutStream)
667
+ Z7_COM_QI_END
668
+ Z7_COM_ADDREF_RELEASE
669
+
670
+ Z7_COM7F_IMP(Write(const void *data, UInt32 size, UInt32 *processedSize))
671
+ Z7_IFACE_COM7_IMP(IOutStream)
672
+
673
+ public:
674
+ bool Open(const std::string& path)
675
+ {
676
+ path_ = NativePath(path);
677
+ file_.open(path_, std::ios::binary | std::ios::trunc);
678
+ return file_.is_open();
679
+ }
680
+
681
+ bool Close()
682
+ {
683
+ file_.flush();
684
+ const bool success = file_.good();
685
+ file_.close();
686
+ return success && !file_.fail();
687
+ }
688
+
689
+ private:
690
+ std::filesystem::path path_;
691
+ std::ofstream file_;
692
+ };
693
+
694
+ Z7_COM7F_IMF(CreateFileOutStream::Write(
695
+ const void *data, UInt32 size, UInt32 *processedSize))
696
+ {
697
+ file_.write(static_cast<const char *>(data), static_cast<std::streamsize>(size));
698
+ if (!file_.good()) {
699
+ return E_FAIL;
700
+ }
701
+ if (processedSize != nullptr) {
702
+ *processedSize = size;
703
+ }
704
+ return S_OK;
705
+ }
706
+
707
+ Z7_COM7F_IMF(CreateFileOutStream::Seek(
708
+ Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
709
+ {
710
+ UInt64 base = 0;
711
+ if (seekOrigin == 1) {
712
+ const auto current = file_.tellp();
713
+ if (current < 0) {
714
+ return E_FAIL;
715
+ }
716
+ base = static_cast<UInt64>(current);
717
+ } else if (seekOrigin == 2) {
718
+ file_.flush();
719
+ if (!file_.good()) {
720
+ return E_FAIL;
721
+ }
722
+ std::error_code error;
723
+ const auto size = std::filesystem::file_size(path_, error);
724
+ if (error) {
725
+ return E_FAIL;
726
+ }
727
+ base = size;
728
+ } else if (seekOrigin != 0) {
729
+ return E_INVALIDARG;
730
+ }
731
+ if (base > static_cast<UInt64>(INT64_MAX)) {
732
+ return E_INVALIDARG;
733
+ }
734
+
735
+ UInt64 target = 0;
736
+ if (offset >= 0) {
737
+ const auto advance = static_cast<UInt64>(offset);
738
+ if (advance > static_cast<UInt64>(INT64_MAX) - base) {
739
+ return E_INVALIDARG;
740
+ }
741
+ target = base + advance;
742
+ } else {
743
+ const auto retreat = static_cast<UInt64>(-(offset + 1)) + 1;
744
+ if (retreat > base) {
745
+ return E_INVALIDARG;
746
+ }
747
+ target = base - retreat;
748
+ }
749
+ file_.clear();
750
+ file_.seekp(static_cast<std::streamoff>(target), std::ios::beg);
751
+ if (!file_.good()) {
752
+ return E_FAIL;
753
+ }
754
+ if (newPosition != nullptr) {
755
+ *newPosition = target;
756
+ }
757
+ return S_OK;
758
+ }
759
+
760
+ Z7_COM7F_IMF(CreateFileOutStream::SetSize(UInt64 newSize))
761
+ {
762
+ if (newSize > static_cast<UInt64>(INT64_MAX)) {
763
+ return E_INVALIDARG;
764
+ }
765
+ file_.flush();
766
+ if (!file_.good()) {
767
+ return E_FAIL;
768
+ }
769
+ const auto current = file_.tellp();
770
+ std::error_code error;
771
+ std::filesystem::resize_file(path_, newSize, error);
772
+ if (error) {
773
+ return E_FAIL;
774
+ }
775
+ if (current >= 0 && static_cast<UInt64>(current) > newSize) {
776
+ file_.clear();
777
+ file_.seekp(static_cast<std::streamoff>(newSize), std::ios::beg);
778
+ if (!file_.good()) {
779
+ return E_FAIL;
780
+ }
781
+ }
782
+ return S_OK;
783
+ }
784
+
634
785
  class ArchiveOpenCallback final :
635
786
  public IArchiveOpenCallback,
636
787
  public ICryptoGetTextPassword,
@@ -673,6 +824,193 @@ Z7_COM7F_IMF(ArchiveOpenCallback::CryptoGetTextPassword(BSTR *password))
673
824
  return GetPasswordBstr(password_, password);
674
825
  }
675
826
 
827
+ class ArchiveUpdateCallback final :
828
+ public IArchiveUpdateCallback,
829
+ public CMyUnknownImp {
830
+ Z7_COM_QI_BEGIN2(IArchiveUpdateCallback)
831
+ Z7_COM_QI_ENTRY(IProgress)
832
+ Z7_COM_QI_END
833
+ Z7_COM_ADDREF_RELEASE
834
+
835
+ Z7_IFACE_COM7_IMP(IProgress)
836
+ Z7_IFACE_COM7_IMP(IArchiveUpdateCallback)
837
+
838
+ public:
839
+ explicit ArchiveUpdateCallback(const std::vector<ArchiveCreateItem>& items)
840
+ : items_(items), operationResult_(NArchive::NUpdate::NOperationResult::kOK) {}
841
+
842
+ Int32 OperationResult() const
843
+ {
844
+ return operationResult_;
845
+ }
846
+
847
+ private:
848
+ const std::vector<ArchiveCreateItem>& items_;
849
+ Int32 operationResult_;
850
+ };
851
+
852
+ Z7_COM7F_IMF(ArchiveUpdateCallback::SetTotal(UInt64))
853
+ {
854
+ return S_OK;
855
+ }
856
+
857
+ Z7_COM7F_IMF(ArchiveUpdateCallback::SetCompleted(const UInt64 *))
858
+ {
859
+ return S_OK;
860
+ }
861
+
862
+ Z7_COM7F_IMF(ArchiveUpdateCallback::GetUpdateItemInfo(
863
+ UInt32 index, Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive))
864
+ {
865
+ if (index >= items_.size()) {
866
+ return E_INVALIDARG;
867
+ }
868
+ if (newData != nullptr) {
869
+ *newData = 1;
870
+ }
871
+ if (newProperties != nullptr) {
872
+ *newProperties = 1;
873
+ }
874
+ if (indexInArchive != nullptr) {
875
+ *indexInArchive = static_cast<UInt32>(-1);
876
+ }
877
+ return S_OK;
878
+ }
879
+
880
+ Z7_COM7F_IMF(ArchiveUpdateCallback::GetProperty(
881
+ UInt32 index, PROPID propertyId, PROPVARIANT *value))
882
+ {
883
+ if (value == nullptr || index >= items_.size()) {
884
+ return E_INVALIDARG;
885
+ }
886
+ NWindows::NCOM::CPropVariant property;
887
+ try {
888
+ const auto& item = items_[index];
889
+ if (propertyId == kpidPath) {
890
+ const auto path = FromUtf8(item.path);
891
+ property = path.c_str();
892
+ } else if (propertyId == kpidIsDir) {
893
+ property = false;
894
+ } else if (propertyId == kpidSize) {
895
+ std::error_code error;
896
+ const auto size = std::filesystem::file_size(NativePath(item.sourcePath), error);
897
+ if (error) {
898
+ return E_FAIL;
899
+ }
900
+ property = static_cast<UInt64>(size);
901
+ }
902
+ } catch (...) {
903
+ return E_INVALIDARG;
904
+ }
905
+ property.Detach(value);
906
+ return S_OK;
907
+ }
908
+
909
+ Z7_COM7F_IMF(ArchiveUpdateCallback::GetStream(
910
+ UInt32 index, ISequentialInStream **inStream))
911
+ {
912
+ if (inStream == nullptr || index >= items_.size()) {
913
+ return E_INVALIDARG;
914
+ }
915
+ *inStream = nullptr;
916
+ auto *fileObject = new CreateFileInStream();
917
+ CMyComPtr<ISequentialInStream> file = fileObject;
918
+ if (!fileObject->Open(items_[index].sourcePath)) {
919
+ return E_FAIL;
920
+ }
921
+ *inStream = file;
922
+ (*inStream)->AddRef();
923
+ return S_OK;
924
+ }
925
+
926
+ Z7_COM7F_IMF(ArchiveUpdateCallback::SetOperationResult(Int32 operationResult))
927
+ {
928
+ if (operationResult != NArchive::NUpdate::NOperationResult::kOK) {
929
+ operationResult_ = operationResult;
930
+ }
931
+ return S_OK;
932
+ }
933
+
934
+ GUID FindOutputArchiveClassId(const std::string& format)
935
+ {
936
+ UInt32 count = 0;
937
+ if (g_getNumberOfFormats(&count) != S_OK) {
938
+ throw std::runtime_error("Unable to enumerate 7-Zip archive handlers");
939
+ }
940
+ for (UInt32 i = 0; i < count; ++i) {
941
+ NWindows::NCOM::CPropVariant name;
942
+ if (g_getHandlerProperty(i, NArchive::NHandlerPropID::kName, &name) != S_OK
943
+ || name.vt != VT_BSTR || LowerAscii(ToUtf8(name.bstrVal)) != format) {
944
+ continue;
945
+ }
946
+ NWindows::NCOM::CPropVariant classIdValue;
947
+ if (g_getHandlerProperty(i, NArchive::NHandlerPropID::kClassID, &classIdValue) != S_OK
948
+ || classIdValue.vt != VT_BSTR
949
+ || ::SysStringByteLen(classIdValue.bstrVal) != sizeof(GUID)) {
950
+ continue;
951
+ }
952
+ GUID classId;
953
+ std::memcpy(&classId, classIdValue.bstrVal, sizeof(classId));
954
+ void *created = nullptr;
955
+ if (g_createObject(&classId, &IID_IOutArchive, &created) == S_OK && created != nullptr) {
956
+ static_cast<IOutArchive *>(created)->Release();
957
+ return classId;
958
+ }
959
+ }
960
+ throw std::runtime_error("The bundled 7-Zip engine has no output handler for format " + format);
961
+ }
962
+
963
+ void CreateArchiveWith7Zip(
964
+ const std::string& outputPath,
965
+ const std::string& format,
966
+ const std::vector<ArchiveCreateItem>& items,
967
+ std::uint32_t compressionLevel,
968
+ const std::string& enginePath)
969
+ {
970
+ EnsureEngineLoaded(enginePath);
971
+ const auto classId = FindOutputArchiveClassId(format);
972
+ void *created = nullptr;
973
+ if (g_createObject(&classId, &IID_IOutArchive, &created) != S_OK || created == nullptr) {
974
+ throw std::runtime_error("Unable to create the 7-Zip output handler");
975
+ }
976
+ CMyComPtr<IOutArchive> archive;
977
+ archive.Attach(static_cast<IOutArchive *>(created));
978
+
979
+ if (format == "7z" || format == "zip") {
980
+ CMyComPtr<ISetProperties> properties;
981
+ if (archive.QueryInterface(IID_ISetProperties, &properties) != S_OK || !properties) {
982
+ throw std::runtime_error("The 7-Zip output handler does not support compression settings");
983
+ }
984
+ const wchar_t *propertyNames[] = {L"x"};
985
+ NWindows::NCOM::CPropVariant propertyValues[1];
986
+ propertyValues[0] = static_cast<UInt32>(compressionLevel);
987
+ if (properties->SetProperties(propertyNames, propertyValues, 1) != S_OK) {
988
+ throw std::runtime_error("The 7-Zip output handler rejected the compression level");
989
+ }
990
+ }
991
+
992
+ auto *outputObject = new CreateFileOutStream();
993
+ CMyComPtr<ISequentialOutStream> output = outputObject;
994
+ if (!outputObject->Open(outputPath)) {
995
+ throw std::runtime_error("Unable to open temporary archive output path");
996
+ }
997
+ auto *callbackObject = new ArchiveUpdateCallback(items);
998
+ CMyComPtr<IArchiveUpdateCallback> callback = callbackObject;
999
+ const auto result = archive->UpdateItems(
1000
+ output,
1001
+ static_cast<UInt32>(items.size()),
1002
+ callback);
1003
+ if (result != S_OK) {
1004
+ throw std::runtime_error("The 7-Zip handler failed while creating the archive");
1005
+ }
1006
+ if (callbackObject->OperationResult() != NArchive::NUpdate::NOperationResult::kOK) {
1007
+ throw std::runtime_error("The 7-Zip handler failed to read one or more source files");
1008
+ }
1009
+ if (!outputObject->Close()) {
1010
+ throw std::runtime_error("Unable to finish writing the archive output");
1011
+ }
1012
+ }
1013
+
676
1014
  class VectorOutStream final : public ISequentialOutStream, public CMyUnknownImp {
677
1015
  Z7_COM_UNKNOWN_IMP_1(ISequentialOutStream)
678
1016
  Z7_COM7F_IMP(Write(const void *data, UInt32 size, UInt32 *processedSize))
@@ -1620,6 +1958,62 @@ private:
1620
1958
  std::string error_;
1621
1959
  };
1622
1960
 
1961
+ class CreateArchiveWorker final : public ExtractionTask {
1962
+ public:
1963
+ CreateArchiveWorker(
1964
+ Napi::Promise::Deferred deferred,
1965
+ std::string outputPath,
1966
+ std::string format,
1967
+ std::vector<ArchiveCreateItem> items,
1968
+ std::uint32_t compressionLevel,
1969
+ std::string enginePath)
1970
+ : deferred_(deferred),
1971
+ outputPath_(std::move(outputPath)),
1972
+ format_(std::move(format)),
1973
+ items_(std::move(items)),
1974
+ compressionLevel_(compressionLevel),
1975
+ enginePath_(std::move(enginePath)) {}
1976
+
1977
+ ~CreateArchiveWorker() override
1978
+ {
1979
+ g_pendingOperations.fetch_sub(1, std::memory_order_acq_rel);
1980
+ }
1981
+
1982
+ void Execute() noexcept override
1983
+ {
1984
+ try {
1985
+ CreateArchiveWith7Zip(
1986
+ outputPath_,
1987
+ format_,
1988
+ items_,
1989
+ compressionLevel_,
1990
+ enginePath_);
1991
+ } catch (const std::exception& error) {
1992
+ error_ = error.what();
1993
+ } catch (...) {
1994
+ error_ = "Unknown native archive creation failure";
1995
+ }
1996
+ }
1997
+
1998
+ void Complete(Napi::Env env) override
1999
+ {
2000
+ if (!error_.empty()) {
2001
+ deferred_.Reject(Napi::Error::New(env, error_).Value());
2002
+ return;
2003
+ }
2004
+ deferred_.Resolve(env.Undefined());
2005
+ }
2006
+
2007
+ private:
2008
+ Napi::Promise::Deferred deferred_;
2009
+ std::string outputPath_;
2010
+ std::string format_;
2011
+ std::vector<ArchiveCreateItem> items_;
2012
+ std::uint32_t compressionLevel_;
2013
+ std::string enginePath_;
2014
+ std::string error_;
2015
+ };
2016
+
1623
2017
  class StreamWorker final : public ExtractionTask {
1624
2018
  public:
1625
2019
  StreamWorker(
@@ -1875,6 +2269,81 @@ Napi::Value Open(const Napi::CallbackInfo& info)
1875
2269
  return deferred.Promise();
1876
2270
  }
1877
2271
 
2272
+ Napi::Value CreateArchive(const Napi::CallbackInfo& info)
2273
+ {
2274
+ const auto env = info.Env();
2275
+ if (info.Length() != 5 || !info[0].IsString() || !info[1].IsString()
2276
+ || !info[2].IsArray() || !info[3].IsNumber() || !info[4].IsString()) {
2277
+ throw Napi::TypeError::New(
2278
+ env, "create(outputPath, format, entries, compressionLevel, enginePath) expects valid arguments");
2279
+ }
2280
+ const auto format = info[1].As<Napi::String>().Utf8Value();
2281
+ if (format != "7z" && format != "zip" && format != "tar" && format != "wim") {
2282
+ throw Napi::TypeError::New(env, "Archive format must be 7z, zip, tar, or wim");
2283
+ }
2284
+ const double levelValue = info[3].As<Napi::Number>().DoubleValue();
2285
+ if (!std::isfinite(levelValue) || std::floor(levelValue) != levelValue
2286
+ || levelValue < 0 || levelValue > 9) {
2287
+ throw Napi::RangeError::New(env, "Compression level must be an integer between 0 and 9");
2288
+ }
2289
+ const auto inputEntries = info[2].As<Napi::Array>();
2290
+ if (inputEntries.Length() > kMaxEntries) {
2291
+ throw Napi::RangeError::New(env, "Archive creation exceeds the native entry limit");
2292
+ }
2293
+ std::vector<ArchiveCreateItem> entries;
2294
+ entries.reserve(inputEntries.Length());
2295
+ for (std::uint32_t i = 0; i < inputEntries.Length(); ++i) {
2296
+ const auto value = inputEntries.Get(i);
2297
+ if (!value.IsObject() || value.IsNull()) {
2298
+ throw Napi::TypeError::New(env, "Archive creation entries must be objects");
2299
+ }
2300
+ const auto item = value.As<Napi::Object>();
2301
+ const auto sourcePath = item.Get("sourcePath");
2302
+ const auto archivePath = item.Get("path");
2303
+ if (!sourcePath.IsString() || !archivePath.IsString()) {
2304
+ throw Napi::TypeError::New(
2305
+ env, "Archive creation entries require string sourcePath and path values");
2306
+ }
2307
+ entries.push_back({
2308
+ sourcePath.As<Napi::String>().Utf8Value(),
2309
+ archivePath.As<Napi::String>().Utf8Value(),
2310
+ });
2311
+ }
2312
+ if (!ReserveOperation()) {
2313
+ throw Napi::Error::New(env, "Native operation queue is full");
2314
+ }
2315
+ auto deferred = Napi::Promise::Deferred::New(env);
2316
+ CreateArchiveWorker *worker = nullptr;
2317
+ try {
2318
+ if (g_extractionPool == nullptr) {
2319
+ throw std::runtime_error("Native extraction pool is not initialized");
2320
+ }
2321
+ worker = new CreateArchiveWorker(
2322
+ deferred,
2323
+ info[0].As<Napi::String>().Utf8Value(),
2324
+ format,
2325
+ std::move(entries),
2326
+ static_cast<std::uint32_t>(levelValue),
2327
+ info[4].As<Napi::String>().Utf8Value());
2328
+ g_extractionPool->Queue(env, worker);
2329
+ } catch (const std::exception& error) {
2330
+ if (worker != nullptr) {
2331
+ delete worker;
2332
+ } else {
2333
+ g_pendingOperations.fetch_sub(1, std::memory_order_acq_rel);
2334
+ }
2335
+ throw Napi::Error::New(env, error.what());
2336
+ } catch (...) {
2337
+ if (worker != nullptr) {
2338
+ delete worker;
2339
+ } else {
2340
+ g_pendingOperations.fetch_sub(1, std::memory_order_acq_rel);
2341
+ }
2342
+ throw Napi::Error::New(env, "Unable to queue native archive creation");
2343
+ }
2344
+ return deferred.Promise();
2345
+ }
2346
+
1878
2347
  Napi::Value Entries(const Napi::CallbackInfo& info)
1879
2348
  {
1880
2349
  const auto env = info.Env();
@@ -2138,6 +2607,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports)
2138
2607
  throw;
2139
2608
  }
2140
2609
  exports.Set("open", Napi::Function::New(env, Open));
2610
+ exports.Set("create", Napi::Function::New(env, CreateArchive));
2141
2611
  exports.Set("entries", Napi::Function::New(env, Entries));
2142
2612
  exports.Set("extract", Napi::Function::New(env, Extract));
2143
2613
  exports.Set("startStream", Napi::Function::New(env, StartStream));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "7zip-native",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Native 7zip bindings for Node.js",
5
5
  "main": "./dist/index.cjs",
6
6
  "type": "module",
@@ -30,6 +30,7 @@
30
30
  "dist/",
31
31
  "prebuilds/",
32
32
  "native/",
33
+ "scripts/",
33
34
  "vendor/7zip/",
34
35
  "binding.gyp",
35
36
  "README.md",
@@ -48,14 +49,16 @@
48
49
  "url": "https://github.com/sponsors/ollm"
49
50
  },
50
51
  "scripts": {
52
+ "install": "node scripts/verify-prebuild.mjs",
51
53
  "prepare": "npm run build && node scripts/clean-upstream-build.mjs",
52
54
  "build": "npm run build:native && npm run eslint && npx rollup -c && npm run typecheck",
53
55
  "build:native": "node scripts/build-native.mjs",
54
56
  "eslint": "eslint ./src --ext .ts,.mts,.js,.mjs",
55
- "typecheck": "npx tsc --noEmit && npx tsc --ignoreConfig --noEmit --allowImportingTsExtensions --target ESNext --module NodeNext --moduleResolution NodeNext --types node bench/benchmark.mts bench/compare.mts bench/concurrency.mts test/archive.test.mts examples/basic.mts",
57
+ "typecheck": "npx tsc --noEmit && npx tsc --ignoreConfig --noEmit --allowImportingTsExtensions --target ESNext --module NodeNext --moduleResolution NodeNext --types node bench/benchmark.mts bench/compare.mts bench/concurrency.mts bench/folder-concurrency.mts test/archive.test.mts examples/basic.mts",
56
58
  "test": "node --test test/*.test.mts",
57
59
  "benchmark": "node --experimental-strip-types bench/benchmark.mts",
58
60
  "benchmark:concurrency": "node --experimental-strip-types bench/concurrency.mts",
61
+ "benchmark:folder-concurrency": "node --experimental-strip-types bench/folder-concurrency.mts",
59
62
  "benchmark:compare": "node --experimental-strip-types bench/compare.mts"
60
63
  },
61
64
  "keywords": [
Binary file
Binary file
@@ -0,0 +1,119 @@
1
+ import {spawn} from 'node:child_process';
2
+ import { copyFile, mkdir, rm } from 'node:fs/promises';
3
+ import {createRequire} from 'node:module';
4
+ import {availableParallelism} from 'node:os';
5
+ import { join, resolve } from 'node:path';
6
+ import {fileURLToPath} from 'node:url';
7
+
8
+ const root = resolve(fileURLToPath(new URL('..', import.meta.url)));
9
+ const bundleDirectory = join(root, 'vendor', '7zip', 'CPP', '7zip', 'Bundles', 'Format7zF');
10
+ const releaseDirectory = join(root, 'build', 'Release');
11
+ const targetArch = process.env.BUILD_ARCH || process.arch;
12
+ const target = `${process.platform}-${targetArch}`;
13
+ const engineName = process.platform === 'win32'
14
+ ? '7z.dll'
15
+ : process.platform === 'darwin' ? 'lib7z.dylib' : '7z.so';
16
+ const require = createRequire(import.meta.url);
17
+
18
+ function run(command, args, cwd, env = process.env)
19
+ {
20
+ return new Promise((resolvePromise, rejectPromise) => {
21
+ const child = spawn(command, args, {cwd, env, stdio: 'inherit'});
22
+ child.once('error', rejectPromise);
23
+ child.once('close', (code, signal) => {
24
+ if(code === 0)
25
+ {
26
+ resolvePromise();
27
+ }
28
+ else
29
+ {
30
+ rejectPromise(new Error(`${command} exited with ${signal ?? code}`));
31
+ }
32
+ });
33
+ });
34
+ }
35
+
36
+ if(process.platform === 'win32')
37
+ {
38
+ const upstreamArch = targetArch === 'ia32' ? 'x86' : targetArch;
39
+ await run('nmake', ['/f', 'makefile', `PLATFORM=${upstreamArch}`], bundleDirectory);
40
+ }
41
+ else
42
+ {
43
+ const args = [
44
+ '-C',
45
+ bundleDirectory,
46
+ '-f',
47
+ 'makefile.gcc',
48
+ `-j${process.env.BUILD_JOBS ?? Math.min(availableParallelism(), 2)}`,
49
+ ];
50
+ const buildEnv = {...process.env};
51
+ if(targetArch === 'ia32')
52
+ {
53
+ args.push('MY_ARCH=-m32', 'CC=gcc', 'CXX=g++');
54
+ buildEnv.CXXFLAGS = `${buildEnv.CXXFLAGS ?? ''} -m32`.trim();
55
+ buildEnv.LDFLAGS = `${buildEnv.LDFLAGS ?? ''} -m32`.trim();
56
+ }
57
+ else if(targetArch === 'arm')
58
+ {
59
+ const compilerPrefix = 'arm-linux-gnueabihf-';
60
+ args.push(
61
+ `CC=${compilerPrefix}gcc`,
62
+ `CXX=${compilerPrefix}g++`,
63
+ 'MY_ARCH=-march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard',
64
+ );
65
+ buildEnv.CC = `${compilerPrefix}gcc`;
66
+ buildEnv.CXX = `${compilerPrefix}g++`;
67
+ buildEnv.CXXFLAGS = `${buildEnv.CXXFLAGS ?? ''} -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard`.trim();
68
+ buildEnv.LDFLAGS = `${buildEnv.LDFLAGS ?? ''} -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard`.trim();
69
+ }
70
+ if(process.platform === 'darwin')
71
+ {
72
+ args.push(
73
+ 'LIB2=-lpthread',
74
+ 'PROG=lib7z',
75
+ 'SHARED_EXT=.dylib',
76
+ 'LDFLAGS=-shared -fPIC -Wl,-install_name,@rpath/lib7z.dylib',
77
+ );
78
+ }
79
+ await run('make', args, root, buildEnv);
80
+ }
81
+
82
+ const upstreamEnginePath = process.platform === 'win32'
83
+ ? join(bundleDirectory, targetArch === 'ia32' ? 'x86' : targetArch, engineName)
84
+ : join(bundleDirectory, '_o', engineName);
85
+ const nodeGypArgs = [
86
+ require.resolve('node-gyp/bin/node-gyp.js'),
87
+ 'rebuild',
88
+ ];
89
+ const nodeGypEnv = {...process.env};
90
+ if(targetArch !== process.arch)
91
+ {
92
+ nodeGypArgs.push(`--arch=${targetArch}`);
93
+ if(targetArch === 'ia32')
94
+ {
95
+ nodeGypEnv.CC = 'gcc';
96
+ nodeGypEnv.CXX = 'g++';
97
+ nodeGypEnv.CXXFLAGS = `${nodeGypEnv.CXXFLAGS ?? ''} -m32`.trim();
98
+ nodeGypEnv.LDFLAGS = `${nodeGypEnv.LDFLAGS ?? ''} -m32`.trim();
99
+ }
100
+ else if(targetArch === 'arm')
101
+ {
102
+ nodeGypEnv.CC = 'arm-linux-gnueabihf-gcc';
103
+ nodeGypEnv.CXX = 'arm-linux-gnueabihf-g++';
104
+ nodeGypEnv.CXXFLAGS = `${nodeGypEnv.CXXFLAGS ?? ''} -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard`.trim();
105
+ nodeGypEnv.LDFLAGS = `${nodeGypEnv.LDFLAGS ?? ''} -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard`.trim();
106
+ }
107
+ }
108
+ await run(process.execPath, nodeGypArgs, root, nodeGypEnv);
109
+
110
+ await mkdir(releaseDirectory, {recursive: true});
111
+ const releaseEnginePath = join(releaseDirectory, engineName);
112
+ await copyFile(upstreamEnginePath, releaseEnginePath);
113
+ const prebuildDirectory = join(root, 'prebuilds', target);
114
+ await rm(prebuildDirectory, {recursive: true, force: true});
115
+ await mkdir(prebuildDirectory, {recursive: true});
116
+ await Promise.all([
117
+ copyFile(join(releaseDirectory, '7zip_native.node'), join(prebuildDirectory, '7zip_native.node')),
118
+ copyFile(releaseEnginePath, join(prebuildDirectory, engineName)),
119
+ ]);
@@ -0,0 +1,9 @@
1
+ import {rm} from 'node:fs/promises';
2
+ import {fileURLToPath} from 'node:url';
3
+
4
+ const upstreamBuildDirectory = new URL(
5
+ '../vendor/7zip/CPP/7zip/Bundles/Format7zF/_o/',
6
+ import.meta.url,
7
+ );
8
+
9
+ await rm(fileURLToPath(upstreamBuildDirectory), {recursive: true, force: true});