dmg-builder 26.0.16 → 26.0.18

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/out/hdiuil.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hdiuil.js","sourceRoot":"","sources":["../src/hdiuil.ts"],"names":[],"mappings":";;;AAmBA,kDAoBC;AAcD,0BAEC;AAvDD,+CAA+C;AAE/C;;;;;;;;;;;;;;GAcG;AACU,QAAA,yBAAyB,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;AAEzE,SAAgB,mBAAmB,CAAC,SAAiB;;IACnD,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAA;IACjC,MAAM,QAAQ,GAA2B;QACvC,GAAG,EAAE,uDAAuD;QAC5D,GAAG,EAAE,2GAA2G;QAChH,GAAG,EAAE,gEAAgE;QACrE,GAAG,EAAE,oGAAoG;QACzG,GAAG,EAAE,8DAA8D;QACnE,IAAI,EAAE,gFAAgF;QACtF,IAAI,EAAE,0EAA0E;QAChF,IAAI,EAAE,0EAA0E;QAChF,IAAI,EAAE,+EAA+E;QACrF,KAAK,EAAE,kEAAkE;QACzE,KAAK,EAAE,0EAA0E;QACjF,OAAO,EAAE,qEAAqE;QAC9E,OAAO,EAAE,oFAAoF;QAC7F,OAAO,EAAE,gFAAgF;KAC1F,CAAA;IAED,OAAO,MAAA,QAAQ,CAAC,IAAI,CAAC,mCAAI,uBAAuB,IAAI,+GAA+G,CAAA;AACrK,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,IAAc,EAAE,EAAE,CAAC,CAAC,KAAU,EAAE,EAAE;;IACrD,MAAM,IAAI,GAAG,MAAA,KAAK,CAAC,IAAI,mCAAI,CAAC,CAAC,CAAA;IAC7B,MAAM,MAAM,GAAG,CAAA,MAAA,KAAK,CAAC,MAAM,0CAAE,QAAQ,EAAE,KAAI,EAAE,CAAA;IAC7C,MAAM,MAAM,GAAG,CAAA,MAAA,KAAK,CAAC,MAAM,0CAAE,QAAQ,EAAE,KAAI,EAAE,CAAA;IAC7C,MAAM,MAAM,GAAG,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC,IAAI,EAAE,CAAA;IAE3C,MAAM,SAAS,GAAG,iCAAyB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACrD,kBAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,kBAAkB,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAE1F,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAEM,KAAK,UAAU,OAAO,CAAC,IAAc;IAC1C,OAAO,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,IAAA,mBAAI,EAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAA;AAChF,CAAC","sourcesContent":["import { exec, log, retry } from \"builder-util\"\n\n/**\n * Table of hdiutil error codes that are transient and can be retried.\n * These codes are typically related to resource availability or temporary issues.\n *\n| Code | Meaning | Why Retry? |\n| ------- | -------------------------------- | ---------------------------------------------------- |\n| `1` | Generic error | Can occur from brief race conditions or temp issues. |\n| `16` | **Resource busy** | Volume is in use — wait and retry often works. |\n| `35` | **Operation timed out** | System delay or timeout — retry after a short delay. |\n| `256` | Volume in use or unmount failure | Same as 16 — usually resolves after retry. |\n| `49153` | Volume not mounted yet | Attach may be too fast — retry after delay. |\n| `-5341` | Disk image too small | Retry *after fixing* with a larger `-size`. |\n| `-5342` | Specified size too small | Same as above — retry if size is corrected. |\n *\n */\nexport const hdiutilTransientExitCodes = new Set([1, 16, 35, 256, 49153])\n\nexport function explainHdiutilError(errorCode: number): string {\n const code = errorCode.toString()\n const messages: Record<string, string> = {\n \"0\": \"Success: The hdiutil command completed without error.\",\n \"1\": \"Generic error: The operation failed, but the reason is not specific. Check command syntax or permissions.\",\n \"2\": \"No such file or directory: Check if the specified path exists.\",\n \"6\": \"Disk image to resize is not currently attached or not recognized as a valid block device by macOS.\",\n \"8\": \"Exec format error: The file might not be a valid disk image.\",\n \"16\": \"Resource busy: The volume is in use. Try closing files or processes and retry.\",\n \"22\": \"Invalid argument: One or more arguments passed to hdiutil are incorrect.\",\n \"35\": \"Operation timed out: The system was too slow or unresponsive. Try again.\",\n \"36\": \"I/O error: There was a problem reading or writing to disk. Check disk health.\",\n \"100\": \"Image-related error: The disk image may be corrupted or invalid.\",\n \"256\": \"Volume is busy or could not be unmounted. Try again after closing files.\",\n \"49153\": \"Volume not mounted yet: The image may not have been fully attached.\",\n \"-5341\": \"Disk image too small: hdiutil could not fit the contents. Increase the image size.\",\n \"-5342\": \"Specified size too small: Disk image creation failed due to insufficient size.\",\n }\n\n return messages[code] ?? `Unknown error (code ${code}): Refer to hdiutil documentation or run with -verbose for details by rerunning with env var DEBUG_DEMB=true.`\n}\n\nconst shouldRetry = (args: string[]) => (error: any) => {\n const code = error.code ?? -1\n const stderr = error.stderr?.toString() || \"\"\n const stdout = error.stdout?.toString() || \"\"\n const output = `${stdout} ${stderr}`.trim()\n\n const willRetry = hdiutilTransientExitCodes.has(code)\n log.warn({ willRetry, args, code, output }, `hdiutil error: ${explainHdiutilError(code)}`)\n\n return willRetry\n}\n\nexport async function hdiUtil(args: string[]): Promise<string | null> {\n return retry(() => exec(\"hdiutil\", args), 5, 5000, 2000, 0, shouldRetry(args))\n}\n"]}
1
+ {"version":3,"file":"hdiuil.js","sourceRoot":"","sources":["../src/hdiuil.ts"],"names":[],"mappings":";;;AAmBA,kDAoBC;AAcD,0BAEC;AAvDD,+CAA+C;AAE/C;;;;;;;;;;;;;;GAcG;AACU,QAAA,yBAAyB,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;AAEzE,SAAgB,mBAAmB,CAAC,SAAiB;;IACnD,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAA;IACjC,MAAM,QAAQ,GAA2B;QACvC,GAAG,EAAE,uDAAuD;QAC5D,GAAG,EAAE,2GAA2G;QAChH,GAAG,EAAE,gEAAgE;QACrE,GAAG,EAAE,oGAAoG;QACzG,GAAG,EAAE,8DAA8D;QACnE,IAAI,EAAE,gFAAgF;QACtF,IAAI,EAAE,0EAA0E;QAChF,IAAI,EAAE,0EAA0E;QAChF,IAAI,EAAE,+EAA+E;QACrF,KAAK,EAAE,kEAAkE;QACzE,KAAK,EAAE,0EAA0E;QACjF,OAAO,EAAE,qEAAqE;QAC9E,OAAO,EAAE,oFAAoF;QAC7F,OAAO,EAAE,gFAAgF;KAC1F,CAAA;IAED,OAAO,MAAA,QAAQ,CAAC,IAAI,CAAC,mCAAI,uBAAuB,IAAI,+GAA+G,CAAA;AACrK,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,IAAc,EAAE,EAAE,CAAC,CAAC,KAAU,EAAE,EAAE;;IACrD,MAAM,IAAI,GAAG,MAAA,KAAK,CAAC,IAAI,mCAAI,CAAC,CAAC,CAAA;IAC7B,MAAM,MAAM,GAAG,CAAA,MAAA,KAAK,CAAC,MAAM,0CAAE,QAAQ,EAAE,KAAI,EAAE,CAAA;IAC7C,MAAM,MAAM,GAAG,CAAA,MAAA,KAAK,CAAC,MAAM,0CAAE,QAAQ,EAAE,KAAI,EAAE,CAAA;IAC7C,MAAM,MAAM,GAAG,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC,IAAI,EAAE,CAAA;IAE3C,MAAM,SAAS,GAAG,iCAAyB,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;IAChE,kBAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,kBAAkB,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAE1F,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAEM,KAAK,UAAU,OAAO,CAAC,IAAc;IAC1C,OAAO,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,IAAA,mBAAI,EAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAA;AAChF,CAAC","sourcesContent":["import { exec, log, retry } from \"builder-util\"\n\n/**\n * Table of hdiutil error codes that are transient and can be retried.\n * These codes are typically related to resource availability or temporary issues.\n *\n| Code | Meaning | Why Retry? |\n| ------- | -------------------------------- | ---------------------------------------------------- |\n| `1` | Generic error | Can occur from brief race conditions or temp issues. |\n| `16` | **Resource busy** | Volume is in use — wait and retry often works. |\n| `35` | **Operation timed out** | System delay or timeout — retry after a short delay. |\n| `256` | Volume in use or unmount failure | Same as 16 — usually resolves after retry. |\n| `49153` | Volume not mounted yet | Attach may be too fast — retry after delay. |\n| `-5341` | Disk image too small | Retry *after fixing* with a larger `-size`. |\n| `-5342` | Specified size too small | Same as above — retry if size is corrected. |\n *\n */\nexport const hdiutilTransientExitCodes = new Set([1, 16, 35, 256, 49153])\n\nexport function explainHdiutilError(errorCode: number): string {\n const code = errorCode.toString()\n const messages: Record<string, string> = {\n \"0\": \"Success: The hdiutil command completed without error.\",\n \"1\": \"Generic error: The operation failed, but the reason is not specific. Check command syntax or permissions.\",\n \"2\": \"No such file or directory: Check if the specified path exists.\",\n \"6\": \"Disk image to resize is not currently attached or not recognized as a valid block device by macOS.\",\n \"8\": \"Exec format error: The file might not be a valid disk image.\",\n \"16\": \"Resource busy: The volume is in use. Try closing files or processes and retry.\",\n \"22\": \"Invalid argument: One or more arguments passed to hdiutil are incorrect.\",\n \"35\": \"Operation timed out: The system was too slow or unresponsive. Try again.\",\n \"36\": \"I/O error: There was a problem reading or writing to disk. Check disk health.\",\n \"100\": \"Image-related error: The disk image may be corrupted or invalid.\",\n \"256\": \"Volume is busy or could not be unmounted. Try again after closing files.\",\n \"49153\": \"Volume not mounted yet: The image may not have been fully attached.\",\n \"-5341\": \"Disk image too small: hdiutil could not fit the contents. Increase the image size.\",\n \"-5342\": \"Specified size too small: Disk image creation failed due to insufficient size.\",\n }\n\n return messages[code] ?? `Unknown error (code ${code}): Refer to hdiutil documentation or run with -verbose for details by rerunning with env var DEBUG_DEMB=true.`\n}\n\nconst shouldRetry = (args: string[]) => (error: any) => {\n const code = error.code ?? -1\n const stderr = error.stderr?.toString() || \"\"\n const stdout = error.stdout?.toString() || \"\"\n const output = `${stdout} ${stderr}`.trim()\n\n const willRetry = hdiutilTransientExitCodes.has(code.toString())\n log.warn({ willRetry, args, code, output }, `hdiutil error: ${explainHdiutilError(code)}`)\n\n return willRetry\n}\n\nexport async function hdiUtil(args: string[]): Promise<string | null> {\n return retry(() => exec(\"hdiutil\", args), 5, 5000, 2000, 0, shouldRetry(args))\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dmg-builder",
3
- "version": "26.0.16",
3
+ "version": "26.0.18",
4
4
  "main": "out/dmgUtil.js",
5
5
  "author": "Vladimir Krivosheev",
6
6
  "license": "MIT",
@@ -20,9 +20,8 @@
20
20
  "fs-extra": "^10.1.0",
21
21
  "iconv-lite": "^0.6.2",
22
22
  "js-yaml": "^4.1.0",
23
- "app-builder-lib": "26.0.16",
24
- "builder-util": "26.0.16",
25
- "builder-util-runtime": "9.3.2"
23
+ "app-builder-lib": "26.0.18",
24
+ "builder-util": "26.0.17"
26
25
  },
27
26
  "optionalDependencies": {
28
27
  "dmg-license": "^1.0.11"
@@ -15,12 +15,12 @@ Data object. The value must be a string.
15
15
 
16
16
  Date values can only be datetime.datetime objects.
17
17
 
18
- The exceptions InvalidPlistException and NotBinaryPlistException may be
18
+ The exceptions InvalidPlistException and NotBinaryPlistException may be
19
19
  thrown to indicate that the data cannot be serialized or deserialized as
20
20
  a binary plist.
21
21
 
22
22
  Plist generation example:
23
-
23
+
24
24
  from biplist import *
25
25
  from datetime import datetime
26
26
  plist = {'aKey':'aValue',
@@ -84,24 +84,24 @@ class Uid(object):
84
84
  integer = 0
85
85
  def __init__(self, integer):
86
86
  self.integer = integer
87
-
87
+
88
88
  def __repr__(self):
89
89
  return "Uid(%d)" % self.integer
90
-
90
+
91
91
  def __eq__(self, other):
92
92
  if isinstance(self, Uid) and isinstance(other, Uid):
93
93
  return self.integer == other.integer
94
94
  return False
95
-
95
+
96
96
  def __cmp__(self, other):
97
97
  return self.integer - other.integer
98
-
98
+
99
99
  def __lt__(self, other):
100
100
  return self.integer < other.integer
101
-
101
+
102
102
  def __hash__(self):
103
103
  return self.integer
104
-
104
+
105
105
  def __int__(self):
106
106
  return int(self.integer)
107
107
 
@@ -224,22 +224,22 @@ class PlistReader(object):
224
224
  currentOffset = 0
225
225
  # Used to detect recursive object references.
226
226
  offsetsStack = []
227
-
227
+
228
228
  def __init__(self, fileOrStream):
229
229
  """Raises NotBinaryPlistException."""
230
230
  self.reset()
231
231
  self.file = fileOrStream
232
-
232
+
233
233
  def parse(self):
234
234
  return self.readRoot()
235
-
235
+
236
236
  def reset(self):
237
237
  self.trailer = None
238
238
  self.contents = ''
239
239
  self.offsets = []
240
240
  self.currentOffset = 0
241
241
  self.offsetsStack = []
242
-
242
+
243
243
  def readRoot(self):
244
244
  result = None
245
245
  self.reset()
@@ -253,38 +253,38 @@ class PlistReader(object):
253
253
  trailerContents = self.contents[-32:]
254
254
  try:
255
255
  self.trailer = PlistTrailer._make(unpack("!xxxxxxBBQQQ", trailerContents))
256
-
256
+
257
257
  if pow(2, self.trailer.offsetSize*8) < self.trailer.offsetTableOffset:
258
258
  raise InvalidPlistException("Offset size insufficient to reference all objects.")
259
-
259
+
260
260
  if pow(2, self.trailer.objectRefSize*8) < self.trailer.offsetCount:
261
261
  raise InvalidPlistException("Too many offsets to represent in size of object reference representation.")
262
-
262
+
263
263
  offset_size = self.trailer.offsetSize * self.trailer.offsetCount
264
264
  offset = self.trailer.offsetTableOffset
265
-
265
+
266
266
  if offset + offset_size > pow(2, 64):
267
267
  raise InvalidPlistException("Offset table is excessively long.")
268
-
268
+
269
269
  if self.trailer.offsetSize > 16:
270
270
  raise InvalidPlistException("Offset size is greater than maximum integer size.")
271
-
271
+
272
272
  if self.trailer.objectRefSize == 0:
273
273
  raise InvalidPlistException("Object reference size is zero.")
274
-
274
+
275
275
  if offset >= len(self.contents) - 32:
276
276
  raise InvalidPlistException("Offset table offset is too large.")
277
-
277
+
278
278
  if offset < len("bplist00x"):
279
279
  raise InvalidPlistException("Offset table offset is too small.")
280
-
280
+
281
281
  if self.trailer.topLevelObjectNumber >= self.trailer.offsetCount:
282
282
  raise InvalidPlistException("Top level object number is larger than the number of objects.")
283
-
283
+
284
284
  offset_contents = self.contents[offset:offset+offset_size]
285
285
  offset_i = 0
286
286
  offset_table_length = len(offset_contents)
287
-
287
+
288
288
  while offset_i < self.trailer.offsetCount:
289
289
  begin = self.trailer.offsetSize*offset_i
290
290
  end = begin+self.trailer.offsetSize
@@ -299,25 +299,25 @@ class PlistReader(object):
299
299
  except TypeError as e:
300
300
  raise InvalidPlistException(e)
301
301
  return result
302
-
302
+
303
303
  def setCurrentOffsetToObjectNumber(self, objectNumber):
304
304
  if objectNumber > len(self.offsets) - 1:
305
305
  raise InvalidPlistException("Invalid offset number: %d" % objectNumber)
306
306
  self.currentOffset = self.offsets[objectNumber]
307
307
  if self.currentOffset in self.offsetsStack:
308
308
  raise InvalidPlistException("Recursive data structure detected in object: %d" % objectNumber)
309
-
309
+
310
310
  def beginOffsetProtection(self):
311
311
  self.offsetsStack.append(self.currentOffset)
312
312
  return self.currentOffset
313
-
313
+
314
314
  def endOffsetProtection(self, offset):
315
315
  try:
316
316
  index = self.offsetsStack.index(offset)
317
317
  self.offsetsStack = self.offsetsStack[:index]
318
318
  except ValueError as e:
319
319
  pass
320
-
320
+
321
321
  def readObject(self):
322
322
  protection = self.beginOffsetProtection()
323
323
  result = None
@@ -328,12 +328,12 @@ class PlistReader(object):
328
328
  format = (marker_byte >> 4) & 0x0f
329
329
  extra = marker_byte & 0x0f
330
330
  self.currentOffset += 1
331
-
331
+
332
332
  def proc_extra(extra):
333
333
  if extra == 0b1111:
334
334
  extra = self.readObject()
335
335
  return extra
336
-
336
+
337
337
  # bool, null, or fill byte
338
338
  if format == 0b0000:
339
339
  if extra == 0b0000:
@@ -382,11 +382,11 @@ class PlistReader(object):
382
382
  elif format == 0b1101:
383
383
  extra = proc_extra(extra)
384
384
  result = self.readDict(extra)
385
- else:
385
+ else:
386
386
  raise InvalidPlistException("Invalid object found: {format: %s, extra: %s}" % (bin(format), bin(extra)))
387
387
  self.endOffsetProtection(protection)
388
388
  return result
389
-
389
+
390
390
  def readContents(self, length, description="Object contents"):
391
391
  end = self.currentOffset + length
392
392
  if end >= len(self.contents) - 32:
@@ -395,12 +395,12 @@ class PlistReader(object):
395
395
  raise InvalidPlistException("%s length is less than zero" % length)
396
396
  data = self.contents[self.currentOffset:end]
397
397
  return data
398
-
398
+
399
399
  def readInteger(self, byteSize):
400
400
  data = self.readContents(byteSize, "Integer")
401
401
  self.currentOffset = self.currentOffset + byteSize
402
402
  return self.getSizedInteger(data, byteSize, as_number=True)
403
-
403
+
404
404
  def readReal(self, length):
405
405
  to_read = pow(2, length)
406
406
  data = self.readContents(to_read, "Real")
@@ -411,8 +411,8 @@ class PlistReader(object):
411
411
  else:
412
412
  raise InvalidPlistException("Unknown Real of length %d bytes" % to_read)
413
413
  return result
414
-
415
- def readRefs(self, count):
414
+
415
+ def readRefs(self, count):
416
416
  refs = []
417
417
  i = 0
418
418
  while i < count:
@@ -422,7 +422,7 @@ class PlistReader(object):
422
422
  self.currentOffset += self.trailer.objectRefSize
423
423
  i += 1
424
424
  return refs
425
-
425
+
426
426
  def readArray(self, count):
427
427
  if not isinstance(count, (int, long)):
428
428
  raise InvalidPlistException("Count of entries in dict isn't of integer type.")
@@ -435,7 +435,7 @@ class PlistReader(object):
435
435
  result.append(value)
436
436
  i += 1
437
437
  return result
438
-
438
+
439
439
  def readDict(self, count):
440
440
  if not isinstance(count, (int, long)):
441
441
  raise InvalidPlistException("Count of keys/values in dict isn't of integer type.")
@@ -451,7 +451,7 @@ class PlistReader(object):
451
451
  result[key] = value
452
452
  i += 1
453
453
  return result
454
-
454
+
455
455
  def readAsciiString(self, length):
456
456
  if not isinstance(length, (int, long)):
457
457
  raise InvalidPlistException("Length of ASCII string isn't of integer type.")
@@ -459,7 +459,7 @@ class PlistReader(object):
459
459
  result = unpack("!%ds" % length, data)[0]
460
460
  self.currentOffset += length
461
461
  return str(result.decode('ascii'))
462
-
462
+
463
463
  def readUnicode(self, length):
464
464
  if not isinstance(length, (int, long)):
465
465
  raise InvalidPlistException("Length of Unicode string isn't of integer type.")
@@ -467,7 +467,7 @@ class PlistReader(object):
467
467
  data = self.readContents(actual_length, "Unicode string")
468
468
  self.currentOffset += actual_length
469
469
  return data.decode('utf_16_be')
470
-
470
+
471
471
  def readDate(self):
472
472
  data = self.readContents(8, "Date")
473
473
  x = unpack(">d", data)[0]
@@ -483,19 +483,19 @@ class PlistReader(object):
483
483
  result = datetime.datetime.min
484
484
  self.currentOffset += 8
485
485
  return result
486
-
486
+
487
487
  def readData(self, length):
488
488
  if not isinstance(length, (int, long)):
489
489
  raise InvalidPlistException("Length of data isn't of integer type.")
490
490
  result = self.readContents(length, "Data")
491
491
  self.currentOffset += length
492
492
  return Data(result)
493
-
493
+
494
494
  def readUid(self, length):
495
495
  if not isinstance(length, (int, long)):
496
496
  raise InvalidPlistException("Uid length isn't of integer type.")
497
497
  return Uid(self.readInteger(length+1))
498
-
498
+
499
499
  def getSizedInteger(self, data, byteSize, as_number=False):
500
500
  """Numbers of 8 bytes are signed integers when they refer to numbers, but unsigned otherwise."""
501
501
  result = 0
@@ -555,16 +555,16 @@ class FloatWrapper(object):
555
555
 
556
556
  class StringWrapper(object):
557
557
  __instances = {}
558
-
558
+
559
559
  encodedValue = None
560
560
  encoding = None
561
-
561
+
562
562
  def __new__(cls, value):
563
563
  '''Ensure we only have a only one instance for any string,
564
564
  and that we encode ascii as 1-byte-per character when possible'''
565
-
565
+
566
566
  encodedValue = None
567
-
567
+
568
568
  for encoding in ('ascii', 'utf_16_be'):
569
569
  try:
570
570
  encodedValue = value.encode(encoding)
@@ -575,26 +575,26 @@ class StringWrapper(object):
575
575
  cls.__instances[encodedValue].encodedValue = encodedValue
576
576
  cls.__instances[encodedValue].encoding = encoding
577
577
  return cls.__instances[encodedValue]
578
-
578
+
579
579
  raise ValueError('Unable to get ascii or utf_16_be encoding for %s' % repr(value))
580
-
580
+
581
581
  def __len__(self):
582
582
  '''Return roughly the number of characters in this string (half the byte length)'''
583
583
  if self.encoding == 'ascii':
584
584
  return len(self.encodedValue)
585
585
  else:
586
586
  return len(self.encodedValue)//2
587
-
587
+
588
588
  def __lt__(self, other):
589
589
  return self.encodedValue < other.encodedValue
590
-
590
+
591
591
  @property
592
592
  def encodingMarker(self):
593
593
  if self.encoding == 'ascii':
594
594
  return 0b0101
595
595
  else:
596
596
  return 0b0110
597
-
597
+
598
598
  def __repr__(self):
599
599
  return '<StringWrapper (%s): %s>' % (self.encoding, self.encodedValue)
600
600
 
@@ -610,7 +610,7 @@ class PlistWriter(object):
610
610
  wrappedFalse = None
611
611
  # Used to detect recursive object references.
612
612
  objectsStack = []
613
-
613
+
614
614
  def __init__(self, file):
615
615
  self.reset()
616
616
  self.file = file
@@ -620,21 +620,21 @@ class PlistWriter(object):
620
620
  def reset(self):
621
621
  self.byteCounts = PlistByteCounts(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
622
622
  self.trailer = PlistTrailer(0, 0, 0, 0, 0)
623
-
623
+
624
624
  # A set of all the uniques which have been computed.
625
625
  self.computedUniques = set()
626
626
  # A list of all the uniques which have been written.
627
627
  self.writtenReferences = {}
628
628
  # A dict of the positions of the written uniques.
629
629
  self.referencePositions = {}
630
-
630
+
631
631
  self.objectsStack = []
632
-
632
+
633
633
  def positionOfObjectReference(self, obj):
634
634
  """If the given object has been written already, return its
635
635
  position in the offset table. Otherwise, return None."""
636
636
  return self.writtenReferences.get(obj)
637
-
637
+
638
638
  def writeRoot(self, root):
639
639
  """
640
640
  Strategy is:
@@ -657,7 +657,7 @@ class PlistWriter(object):
657
657
  self.trailer = self.trailer._replace(**{'objectRefSize':self.intSize(len(self.computedUniques))})
658
658
  self.writeObjectReference(wrapped_root, output)
659
659
  output = self.writeObject(wrapped_root, output, setReferencePosition=True)
660
-
660
+
661
661
  # output size at this point is an upper bound on how big the
662
662
  # object reference offsets need to be.
663
663
  self.trailer = self.trailer._replace(**{
@@ -666,18 +666,18 @@ class PlistWriter(object):
666
666
  'offsetTableOffset':len(output),
667
667
  'topLevelObjectNumber':0
668
668
  })
669
-
669
+
670
670
  output = self.writeOffsetTable(output)
671
671
  output += pack('!xxxxxxBBQQQ', *self.trailer)
672
672
  self.file.write(output)
673
-
673
+
674
674
  def beginRecursionProtection(self, obj):
675
675
  if not isinstance(obj, (set, dict, list, tuple)):
676
676
  return
677
677
  if id(obj) in self.objectsStack:
678
678
  raise InvalidPlistException("Recursive containers are not allowed in plists.")
679
679
  self.objectsStack.append(id(obj))
680
-
680
+
681
681
  def endRecursionProtection(self, obj):
682
682
  if not isinstance(obj, (set, dict, list, tuple)):
683
683
  return
@@ -690,7 +690,7 @@ class PlistWriter(object):
690
690
  def wrapRoot(self, root):
691
691
  result = None
692
692
  self.beginRecursionProtection(root)
693
-
693
+
694
694
  if isinstance(root, bool):
695
695
  if root is True:
696
696
  result = self.wrappedTrue
@@ -722,7 +722,7 @@ class PlistWriter(object):
722
722
  result = Data(root)
723
723
  else:
724
724
  result = root
725
-
725
+
726
726
  self.endRecursionProtection(root)
727
727
  return result
728
728
 
@@ -737,7 +737,7 @@ class PlistWriter(object):
737
737
  raise InvalidPlistException('Data cannot be dictionary keys in plists.')
738
738
  elif not isinstance(key, StringWrapper):
739
739
  raise InvalidPlistException('Keys must be strings.')
740
-
740
+
741
741
  def proc_size(size):
742
742
  if size > 0b1110:
743
743
  size += self.intSize(size)
@@ -749,7 +749,7 @@ class PlistWriter(object):
749
749
  return
750
750
  else:
751
751
  self.computedUniques.add(obj)
752
-
752
+
753
753
  if obj is None:
754
754
  self.incrementByteCount('nullBytes')
755
755
  elif isinstance(obj, BoolWrapper):
@@ -763,7 +763,7 @@ class PlistWriter(object):
763
763
  elif isinstance(obj, FloatWrapper):
764
764
  size = self.realSize(obj)
765
765
  self.incrementByteCount('realBytes', incr=1+size)
766
- elif isinstance(obj, datetime.datetime):
766
+ elif isinstance(obj, datetime.datetime):
767
767
  self.incrementByteCount('dateBytes', incr=2)
768
768
  elif isinstance(obj, Data):
769
769
  size = proc_size(len(obj))
@@ -823,15 +823,15 @@ class PlistWriter(object):
823
823
  else:
824
824
  result += pack('!B', (format << 4) | length)
825
825
  return result
826
-
826
+
827
827
  def timedelta_total_seconds(td):
828
828
  # Shim for Python 2.6 compatibility, which doesn't have total_seconds.
829
829
  # Make one argument a float to ensure the right calculation.
830
830
  return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10.0**6) / 10.0**6
831
-
831
+
832
832
  if setReferencePosition:
833
833
  self.referencePositions[obj] = len(output)
834
-
834
+
835
835
  if obj is None:
836
836
  output += pack('!B', 0b00000000)
837
837
  elif isinstance(obj, BoolWrapper):
@@ -875,7 +875,7 @@ class PlistWriter(object):
875
875
  output += proc_variable_length(0b1100, len(obj))
876
876
  else:
877
877
  output += proc_variable_length(0b1010, len(obj))
878
-
878
+
879
879
  objectsToWrite = []
880
880
  for objRef in sorted(obj) if isinstance(obj, set) else obj:
881
881
  (isNew, output) = self.writeObjectReference(objRef, output)
@@ -902,7 +902,7 @@ class PlistWriter(object):
902
902
  for objRef in objectsToWrite:
903
903
  output = self.writeObject(objRef, output, setReferencePosition=True)
904
904
  return output
905
-
905
+
906
906
  def writeOffsetTable(self, output):
907
907
  """Writes all of the object reference offsets."""
908
908
  all_positions = []
@@ -922,12 +922,12 @@ class PlistWriter(object):
922
922
  output += self.binaryInt(position, self.trailer.offsetSize)
923
923
  all_positions.append(position)
924
924
  return output
925
-
925
+
926
926
  def binaryReal(self, obj):
927
927
  # just use doubles
928
928
  result = pack('>d', obj.value)
929
929
  return result
930
-
930
+
931
931
  def binaryInt(self, obj, byteSize=None, as_number=False):
932
932
  result = b''
933
933
  if byteSize is None:
@@ -951,7 +951,7 @@ class PlistWriter(object):
951
951
  else:
952
952
  raise InvalidPlistException("Core Foundation can't handle integers with size greater than 16 bytes.")
953
953
  return result
954
-
954
+
955
955
  def intSize(self, obj):
956
956
  """Returns the number of bytes necessary to store the given integer."""
957
957
  # SIGNED
@@ -972,6 +972,6 @@ class PlistWriter(object):
972
972
  return 16
973
973
  else:
974
974
  raise InvalidPlistException("Core Foundation can't handle integers with size greater than 8 bytes.")
975
-
975
+
976
976
  def realSize(self, obj):
977
977
  return 8
@@ -0,0 +1,5 @@
1
+ from .core import build_dmg
2
+
3
+ __version__ = "1.6.5"
4
+
5
+ __all__ = ["__version__", "build_dmg"]
@@ -0,0 +1,67 @@
1
+ #! /usr/bin/env python3
2
+ import argparse
3
+
4
+ from .core import build_dmg
5
+
6
+
7
+ def main():
8
+ parser = argparse.ArgumentParser(description="Construct a disk image file.")
9
+ parser.add_argument(
10
+ "volume_name",
11
+ metavar="volume-name",
12
+ help="The name to give to the volume (this will appear in the title bar when the user mounts the disk image).",
13
+ )
14
+ parser.add_argument(
15
+ "filename",
16
+ metavar="output.dmg",
17
+ help="The filename of the disk image to create.",
18
+ )
19
+ parser.add_argument("-s", "--settings", help="The path of the settings file.")
20
+ parser.add_argument(
21
+ "-D",
22
+ dest="defines",
23
+ action="append",
24
+ default=[],
25
+ help="Define a value for the settings file (e.g. -Dfoo=bar).",
26
+ )
27
+ parser.add_argument(
28
+ "--no-hidpi",
29
+ dest="lookForHiDPI",
30
+ action="store_false",
31
+ default=True,
32
+ help="Do not search for HiDPI versions of the background image (if specified)",
33
+ )
34
+ parser.add_argument(
35
+ "--detach-retries",
36
+ dest="detachRetries",
37
+ type=int,
38
+ default=5,
39
+ choices=range(1, 31),
40
+ help="Number of attempts to detach disk volume (1 sec. interval)",
41
+ )
42
+
43
+ args = parser.parse_args()
44
+
45
+ defines = {}
46
+ for d in args.defines:
47
+ k, v = d.split("=", 1)
48
+ k = k.strip()
49
+ v = v.strip()
50
+ if (v.startswith("'") and v.endswith("'")) or (
51
+ v.startswith('"') and v.endswith('"')
52
+ ):
53
+ v = v[1:-1]
54
+ defines[k] = v
55
+
56
+ build_dmg(
57
+ args.filename,
58
+ args.volume_name,
59
+ args.settings,
60
+ defines=defines,
61
+ lookForHiDPI=args.lookForHiDPI,
62
+ detach_retries=args.detachRetries,
63
+ )
64
+
65
+
66
+ if __name__ == "__main__":
67
+ main()