@joebigelow / wix-1 / commits / 4965ca76

Handle versioned extension ids.

Bob Arnson committed Mar 5, 2020 at 19:52 UTC 4965ca76323d2ee709bc1790ed1e49ab958445b4
2 files changed +311 -269
src/WixToolset.Converters/Wix3Converter.cs
+277 -269
@@ -305,368 +305,376 @@ namespace WixToolset.Converters
305 }
306 }
307
308 - private void ConvertDirectoryElement(XElement element)
309 - {
310 - if (null == element.Attribute("Name"))
308 + private void ConvertDirectoryElement(XElement element)
309 {
312 - var attribute = element.Attribute("ShortName");
313 - if (null != attribute)
310 + if (null == element.Attribute("Name"))
311 {
315 - var shortName = attribute.Value;
316 - if (this.OnError(ConverterTestType.AssignDirectoryNameFromShortName, element, "The directory ShortName attribute is being renamed to Name since Name wasn't specified for value '{0}'", shortName))
312 + var attribute = element.Attribute("ShortName");
313 + if (null != attribute)
314 {
318 - element.Add(new XAttribute("Name", shortName));
319 - attribute.Remove();
315 + var shortName = attribute.Value;
316 + if (this.OnError(ConverterTestType.AssignDirectoryNameFromShortName, element, "The directory ShortName attribute is being renamed to Name since Name wasn't specified for value '{0}'", shortName))
317 + {
318 + element.Add(new XAttribute("Name", shortName));
319 + attribute.Remove();
320 + }
321 }
322 }
323 }
323 - }
324
325 - private void ConvertFileElement(XElement element)
326 - {
327 - if (null == element.Attribute("Id"))
325 + private void ConvertFileElement(XElement element)
326 {
329 - var attribute = element.Attribute("Name");
330 -
331 - if (null == attribute)
327 + if (null == element.Attribute("Id"))
328 {
333 - attribute = element.Attribute("Source");
334 - }
329 + var attribute = element.Attribute("Name");
330
336 - if (null != attribute)
337 - {
338 - var name = Path.GetFileName(attribute.Value);
331 + if (null == attribute)
332 + {
333 + attribute = element.Attribute("Source");
334 + }
335
340 - if (this.OnError(ConverterTestType.AssignAnonymousFileId, element, "The file id is being updated to '{0}' to ensure it remains the same as the default", name))
336 + if (null != attribute)
337 {
342 - IEnumerable<XAttribute> attributes = element.Attributes().ToList();
343 - element.RemoveAttributes();
344 - element.Add(new XAttribute("Id", GetIdentifierFromName(name)));
345 - element.Add(attributes);
338 + var name = Path.GetFileName(attribute.Value);
339 +
340 + if (this.OnError(ConverterTestType.AssignAnonymousFileId, element, "The file id is being updated to '{0}' to ensure it remains the same as the default", name))
341 + {
342 + IEnumerable<XAttribute> attributes = element.Attributes().ToList();
343 + element.RemoveAttributes();
344 + element.Add(new XAttribute("Id", GetIdentifierFromName(name)));
345 + element.Add(attributes);
346 + }
347 }
348 }
349 }
349 - }
350 -
351 - private void ConvertSuppressSignatureValidation(XElement element)
352 - {
353 - var suppressSignatureValidation = element.Attribute("SuppressSignatureValidation");
350
355 - if (null != suppressSignatureValidation)
351 + private void ConvertSuppressSignatureValidation(XElement element)
352 {
357 - if (this.OnError(ConverterTestType.SuppressSignatureValidationDeprecated, element, "The chain package element contains deprecated '{0}' attribute. Use the 'EnableSignatureValidation' attribute instead.", suppressSignatureValidation.Name))
353 + var suppressSignatureValidation = element.Attribute("SuppressSignatureValidation");
354 +
355 + if (null != suppressSignatureValidation)
356 {
359 - if ("no" == suppressSignatureValidation.Value)
357 + if (this.OnError(ConverterTestType.SuppressSignatureValidationDeprecated, element, "The chain package element contains deprecated '{0}' attribute. Use the 'EnableSignatureValidation' attribute instead.", suppressSignatureValidation.Name))
358 {
361 - element.Add(new XAttribute("EnableSignatureValidation", "yes"));
359 + if ("no" == suppressSignatureValidation.Value)
360 + {
361 + element.Add(new XAttribute("EnableSignatureValidation", "yes"));
362 + }
363 }
363 - }
364
365 - suppressSignatureValidation.Remove();
365 + suppressSignatureValidation.Remove();
366 + }
367 }
367 - }
368 -
369 - private void ConvertCustomActionElement(XElement xCustomAction)
370 - {
371 - var xBinaryKey = xCustomAction.Attribute("BinaryKey");
368
373 - if (xBinaryKey?.Value == "WixCA")
369 + private void ConvertCustomActionElement(XElement xCustomAction)
370 {
375 - if (this.OnError(ConverterTestType.WixCABinaryIdRenamed, xCustomAction, "The WixCA custom action DLL Binary table id has been renamed. Use the id 'UtilCA' instead."))
371 + var xBinaryKey = xCustomAction.Attribute("BinaryKey");
372 +
373 + if (xBinaryKey?.Value == "WixCA" || xBinaryKey?.Value == "UtilCA")
374 {
377 - xBinaryKey.Value = "UtilCA";
375 + if (this.OnError(ConverterTestType.WixCABinaryIdRenamed, xCustomAction, "The WixCA custom action DLL Binary table id has been renamed. Use the id 'Wix4UtilCA_X86' instead."))
376 + {
377 + xBinaryKey.Value = "Wix4UtilCA_X86";
378 + }
379 }
379 - }
380 -
381 - var xDllEntry = xCustomAction.Attribute("DllEntry");
380
383 - if (xDllEntry?.Value == "CAQuietExec" || xDllEntry?.Value == "CAQuietExec64")
384 - {
385 - if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The CAQuietExec and CAQuietExec64 custom action ids have been renamed. Use the ids 'WixQuietExec' and 'WixQuietExec64' instead."))
381 + if (xBinaryKey?.Value == "WixCA_x64" || xBinaryKey?.Value == "UtilCA_x64")
382 {
387 - xDllEntry.Value = xDllEntry.Value.Replace("CAQuietExec", "WixQuietExec");
383 + if (this.OnError(ConverterTestType.WixCABinaryIdRenamed, xCustomAction, "The WixCA_x64 custom action DLL Binary table id has been renamed. Use the id 'Wix4UtilCA_X64' instead."))
384 + {
385 + xBinaryKey.Value = "Wix4UtilCA_X64";
386 + }
387 }
389 - }
388
391 - var xProperty = xCustomAction.Attribute("Property");
389 + var xDllEntry = xCustomAction.Attribute("DllEntry");
390
393 - if (xProperty?.Value == "QtExecCmdLine" || xProperty?.Value == "QtExec64CmdLine")
394 - {
395 - if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The QtExecCmdLine and QtExec64CmdLine property ids have been renamed. Use the ids 'WixQuietExecCmdLine' and 'WixQuietExec64CmdLine' instead."))
391 + if (xDllEntry?.Value == "CAQuietExec" || xDllEntry?.Value == "CAQuietExec64")
392 {
397 - xProperty.Value = xProperty.Value.Replace("QtExec", "WixQuietExec");
393 + if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The CAQuietExec and CAQuietExec64 custom action ids have been renamed. Use the ids 'WixQuietExec' and 'WixQuietExec64' instead."))
394 + {
395 + xDllEntry.Value = xDllEntry.Value.Replace("CAQuietExec", "WixQuietExec");
396 + }
397 }
399 - }
400 - }
398
402 - private void ConvertPropertyElement(XElement xProperty)
403 - {
404 - var xId = xProperty.Attribute("Id");
399 + var xProperty = xCustomAction.Attribute("Property");
400
406 - if (xId.Value == "QtExecCmdTimeout")
407 - {
408 - this.OnError(ConverterTestType.QtExecCmdTimeoutAmbiguous, xProperty, "QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout.");
401 + if (xProperty?.Value == "QtExecCmdLine" || xProperty?.Value == "QtExec64CmdLine")
402 + {
403 + if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The QtExecCmdLine and QtExec64CmdLine property ids have been renamed. Use the ids 'WixQuietExecCmdLine' and 'WixQuietExec64CmdLine' instead."))
404 + {
405 + xProperty.Value = xProperty.Value.Replace("QtExec", "WixQuietExec");
406 + }
407 + }
408 }
410 - }
409
412 - /// <summary>
413 - /// Converts a Wix element.
414 - /// </summary>
415 - /// <param name="element">The Wix element to convert.</param>
416 - /// <returns>The converted element.</returns>
417 - private void ConvertElementWithoutNamespace(XElement element)
418 - {
419 - if (this.OnError(ConverterTestType.XmlnsMissing, element, "The xmlns attribute is missing. It must be present with a value of '{0}'.", WixNamespace.NamespaceName))
410 + private void ConvertPropertyElement(XElement xProperty)
411 {
421 - element.Name = WixNamespace.GetName(element.Name.LocalName);
422 -
423 - element.Add(new XAttribute("xmlns", WixNamespace.NamespaceName)); // set the default namespace.
412 + var xId = xProperty.Attribute("Id");
413
425 - foreach (var elementWithoutNamespace in element.DescendantsAndSelf().Where(e => XNamespace.None == e.Name.Namespace))
414 + if (xId.Value == "QtExecCmdTimeout")
415 {
427 - elementWithoutNamespace.Name = WixNamespace.GetName(elementWithoutNamespace.Name.LocalName);
416 + this.OnError(ConverterTestType.QtExecCmdTimeoutAmbiguous, xProperty, "QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout.");
417 }
418 }
430 - }
419
432 - private IEnumerable<ConverterTestType> YieldConverterTypes(IEnumerable<string> types)
433 - {
434 - if (null != types)
420 + /// <summary>
421 + /// Converts a Wix element.
422 + /// </summary>
423 + /// <param name="element">The Wix element to convert.</param>
424 + /// <returns>The converted element.</returns>
425 + private void ConvertElementWithoutNamespace(XElement element)
426 {
436 - foreach (var type in types)
427 + if (this.OnError(ConverterTestType.XmlnsMissing, element, "The xmlns attribute is missing. It must be present with a value of '{0}'.", WixNamespace.NamespaceName))
428 {
429 + element.Name = WixNamespace.GetName(element.Name.LocalName);
430
439 - if (Enum.TryParse<ConverterTestType>(type, true, out var itt))
440 - {
441 - yield return itt;
442 - }
443 - else // not a known ConverterTestType
431 + element.Add(new XAttribute("xmlns", WixNamespace.NamespaceName)); // set the default namespace.
432 +
433 + foreach (var elementWithoutNamespace in element.DescendantsAndSelf().Where(e => XNamespace.None == e.Name.Namespace))
434 {
445 - this.OnError(ConverterTestType.ConverterTestTypeUnknown, null, "Unknown error type: '{0}'.", type);
435 + elementWithoutNamespace.Name = WixNamespace.GetName(elementWithoutNamespace.Name.LocalName);
436 }
437 }
438 }
449 - }
439
451 - private static void UpdateElementsWithDeprecatedNamespaces(IEnumerable<XElement> elements, Dictionary<XNamespace, XNamespace> deprecatedToUpdatedNamespaces)
452 - {
453 - foreach (var element in elements)
440 + private IEnumerable<ConverterTestType> YieldConverterTypes(IEnumerable<string> types)
441 {
455 -
456 - if (deprecatedToUpdatedNamespaces.TryGetValue(element.Name.Namespace, out var ns))
457 - {
458 - element.Name = ns.GetName(element.Name.LocalName);
459 - }
460 -
461 - // Remove all the attributes and add them back to with their namespace updated (as necessary).
462 - IEnumerable<XAttribute> attributes = element.Attributes().ToList();
463 - element.RemoveAttributes();
464 -
465 - foreach (var attribute in attributes)
442 + if (null != types)
443 {
467 - var convertedAttribute = attribute;
468 -
469 - if (attribute.IsNamespaceDeclaration)
444 + foreach (var type in types)
445 {
471 - if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Value, out ns))
446 +
447 + if (Enum.TryParse<ConverterTestType>(type, true, out var itt))
448 {
473 - convertedAttribute = ("xmlns" == attribute.Name.LocalName) ? new XAttribute(attribute.Name.LocalName, ns.NamespaceName) : new XAttribute(XNamespace.Xmlns + attribute.Name.LocalName, ns.NamespaceName);
449 + yield return itt;
450 + }
451 + else // not a known ConverterTestType
452 + {
453 + this.OnError(ConverterTestType.ConverterTestTypeUnknown, null, "Unknown error type: '{0}'.", type);
454 }
455 }
476 - else if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Name.Namespace, out ns))
477 - {
478 - convertedAttribute = new XAttribute(ns.GetName(attribute.Name.LocalName), attribute.Value);
479 - }
480 -
481 - element.Add(convertedAttribute);
456 }
457 }
484 - }
458
486 - /// <summary>
487 - /// Determine if the whitespace preceding a node is appropriate for its depth level.
488 - /// </summary>
489 - /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param>
490 - /// <param name="level">The depth level that should match this whitespace.</param>
491 - /// <param name="whitespace">The whitespace to validate.</param>
492 - /// <returns>true if the whitespace is legal; false otherwise.</returns>
493 - private static bool LeadingWhitespaceValid(int indentationAmount, int level, string whitespace)
494 - {
495 - // Strip off leading newlines; there can be an arbitrary number of these.
496 - whitespace = whitespace.TrimStart(XDocumentNewLine);
497 -
498 - var indentation = new string(' ', level * indentationAmount);
499 -
500 - return whitespace == indentation;
501 - }
502 -
503 - /// <summary>
504 - /// Fix the whitespace in a whitespace node.
505 - /// </summary>
506 - /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param>
507 - /// <param name="level">The depth level of the desired whitespace.</param>
508 - /// <param name="whitespace">The whitespace node to fix.</param>
509 - private static void FixupWhitespace(int indentationAmount, int level, XText whitespace)
510 - {
511 - var value = new StringBuilder(whitespace.Value.Length);
512 -
513 - // Keep any previous preceeding new lines.
514 - var newlines = whitespace.Value.TakeWhile(c => c == XDocumentNewLine).Count();
515 -
516 - // Ensure there is always at least one new line before the indentation.
517 - value.Append(XDocumentNewLine, newlines == 0 ? 1 : newlines);
518 -
519 - whitespace.Value = value.Append(' ', level * indentationAmount).ToString();
520 - }
521 -
522 - /// <summary>
523 - /// Output an error message to the console.
524 - /// </summary>
525 - /// <param name="converterTestType">The type of converter test.</param>
526 - /// <param name="node">The node that caused the error.</param>
527 - /// <param name="message">Detailed error message.</param>
528 - /// <param name="args">Additional formatted string arguments.</param>
529 - /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns>
530 - private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args)
531 - {
532 - if (this.IgnoreErrors.Contains(converterTestType)) // ignore the error
459 + private static void UpdateElementsWithDeprecatedNamespaces(IEnumerable<XElement> elements, Dictionary<XNamespace, XNamespace> deprecatedToUpdatedNamespaces)
460 {
534 - return false;
535 - }
536 -
537 - // Increase the error count.
538 - this.Errors++;
539 -
540 - var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wixcop.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber);
541 - var warning = this.ErrorsAsWarnings.Contains(converterTestType);
542 - var display = String.Format(CultureInfo.CurrentCulture, message, args);
461 + foreach (var element in elements)
462 + {
463
544 - var msg = new Message(sourceLine, warning ? MessageLevel.Warning : MessageLevel.Error, (int)converterTestType, "{0} ({1})", display, converterTestType.ToString());
464 + if (deprecatedToUpdatedNamespaces.TryGetValue(element.Name.Namespace, out var ns))
465 + {
466 + element.Name = ns.GetName(element.Name.LocalName);
467 + }
468
546 - this.Messaging.Write(msg);
469 + // Remove all the attributes and add them back to with their namespace updated (as necessary).
470 + IEnumerable<XAttribute> attributes = element.Attributes().ToList();
471 + element.RemoveAttributes();
472
548 - return true;
549 - }
473 + foreach (var attribute in attributes)
474 + {
475 + var convertedAttribute = attribute;
476
551 - /// <summary>
552 - /// Return an identifier based on passed file/directory name
553 - /// </summary>
554 - /// <param name="name">File/directory name to generate identifer from</param>
555 - /// <returns>A version of the name that is a legal identifier.</returns>
556 - /// <remarks>This is duplicated from WiX's Common class.</remarks>
557 - private static string GetIdentifierFromName(string name)
558 - {
559 - string result = IllegalIdentifierCharacters.Replace(name, "_"); // replace illegal characters with "_".
477 + if (attribute.IsNamespaceDeclaration)
478 + {
479 + if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Value, out ns))
480 + {
481 + convertedAttribute = ("xmlns" == attribute.Name.LocalName) ? new XAttribute(attribute.Name.LocalName, ns.NamespaceName) : new XAttribute(XNamespace.Xmlns + attribute.Name.LocalName, ns.NamespaceName);
482 + }
483 + }
484 + else if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Name.Namespace, out ns))
485 + {
486 + convertedAttribute = new XAttribute(ns.GetName(attribute.Name.LocalName), attribute.Value);
487 + }
488
561 - // MSI identifiers must begin with an alphabetic character or an
562 - // underscore. Prefix all other values with an underscore.
563 - if (AddPrefix.IsMatch(name))
564 - {
565 - result = String.Concat("_", result);
489 + element.Add(convertedAttribute);
490 + }
491 + }
492 }
493
568 - return result;
569 - }
570 -
571 - /// <summary>
572 - /// Converter test types. These are used to condition error messages down to warnings.
573 - /// </summary>
574 - private enum ConverterTestType
575 - {
576 - /// <summary>
577 - /// Internal-only: displayed when a string cannot be converted to an ConverterTestType.
578 - /// </summary>
579 - ConverterTestTypeUnknown,
580 -
494 /// <summary>
582 - /// Displayed when an XML loading exception has occurred.
495 + /// Determine if the whitespace preceding a node is appropriate for its depth level.
496 /// </summary>
584 - XmlException,
585 -
586 - /// <summary>
587 - /// Displayed when a file cannot be accessed; typically when trying to save back a fixed file.
588 - /// </summary>
589 - UnauthorizedAccessException,
497 + /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param>
498 + /// <param name="level">The depth level that should match this whitespace.</param>
499 + /// <param name="whitespace">The whitespace to validate.</param>
500 + /// <returns>true if the whitespace is legal; false otherwise.</returns>
501 + private static bool LeadingWhitespaceValid(int indentationAmount, int level, string whitespace)
502 + {
503 + // Strip off leading newlines; there can be an arbitrary number of these.
504 + whitespace = whitespace.TrimStart(XDocumentNewLine);
505
591 - /// <summary>
592 - /// Displayed when the encoding attribute in the XML declaration is not 'UTF-8'.
593 - /// </summary>
594 - DeclarationEncodingWrong,
506 + var indentation = new string(' ', level * indentationAmount);
507
596 - /// <summary>
597 - /// Displayed when the XML declaration is missing from the source file.
598 - /// </summary>
599 - DeclarationMissing,
508 + return whitespace == indentation;
509 + }
510
511 /// <summary>
602 - /// Displayed when the whitespace preceding a CDATA node is wrong.
512 + /// Fix the whitespace in a whitespace node.
513 /// </summary>
604 - WhitespacePrecedingCDATAWrong,
514 + /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param>
515 + /// <param name="level">The depth level of the desired whitespace.</param>
516 + /// <param name="whitespace">The whitespace node to fix.</param>
517 + private static void FixupWhitespace(int indentationAmount, int level, XText whitespace)
518 + {
519 + var value = new StringBuilder(whitespace.Value.Length);
520
606 - /// <summary>
607 - /// Displayed when the whitespace preceding a node is wrong.
608 - /// </summary>
609 - WhitespacePrecedingNodeWrong,
521 + // Keep any previous preceeding new lines.
522 + var newlines = whitespace.Value.TakeWhile(c => c == XDocumentNewLine).Count();
523
611 - /// <summary>
612 - /// Displayed when an element is not empty as it should be.
613 - /// </summary>
614 - NotEmptyElement,
524 + // Ensure there is always at least one new line before the indentation.
525 + value.Append(XDocumentNewLine, newlines == 0 ? 1 : newlines);
526
616 - /// <summary>
617 - /// Displayed when the whitespace following a CDATA node is wrong.
618 - /// </summary>
619 - WhitespaceFollowingCDATAWrong,
527 + whitespace.Value = value.Append(' ', level * indentationAmount).ToString();
528 + }
529
530 /// <summary>
622 - /// Displayed when the whitespace preceding an end element is wrong.
531 + /// Output an error message to the console.
532 /// </summary>
624 - WhitespacePrecedingEndElementWrong,
533 + /// <param name="converterTestType">The type of converter test.</param>
534 + /// <param name="node">The node that caused the error.</param>
535 + /// <param name="message">Detailed error message.</param>
536 + /// <param name="args">Additional formatted string arguments.</param>
537 + /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns>
538 + private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args)
539 + {
540 + if (this.IgnoreErrors.Contains(converterTestType)) // ignore the error
541 + {
542 + return false;
543 + }
544
626 - /// <summary>
627 - /// Displayed when the xmlns attribute is missing from the document element.
628 - /// </summary>
629 - XmlnsMissing,
545 + // Increase the error count.
546 + this.Errors++;
547
631 - /// <summary>
632 - /// Displayed when the xmlns attribute on the document element is wrong.
633 - /// </summary>
634 - XmlnsValueWrong,
548 + var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wixcop.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber);
549 + var warning = this.ErrorsAsWarnings.Contains(converterTestType);
550 + var display = String.Format(CultureInfo.CurrentCulture, message, args);
551
636 - /// <summary>
637 - /// Assign an identifier to a File element when on Id attribute is specified.
638 - /// </summary>
639 - AssignAnonymousFileId,
552 + var msg = new Message(sourceLine, warning ? MessageLevel.Warning : MessageLevel.Error, (int)converterTestType, "{0} ({1})", display, converterTestType.ToString());
553
641 - /// <summary>
642 - /// SuppressSignatureValidation attribute is deprecated and replaced with EnableSignatureValidation.
643 - /// </summary>
644 - SuppressSignatureValidationDeprecated,
554 + this.Messaging.Write(msg);
555
646 - /// <summary>
647 - /// WixCA Binary/@Id has been renamed to UtilCA.
648 - /// </summary>
649 - WixCABinaryIdRenamed,
556 + return true;
557 + }
558
559 /// <summary>
652 - /// QtExec custom actions have been renamed.
560 + /// Return an identifier based on passed file/directory name
561 /// </summary>
654 - QuietExecCustomActionsRenamed,
562 + /// <param name="name">File/directory name to generate identifer from</param>
563 + /// <returns>A version of the name that is a legal identifier.</returns>
564 + /// <remarks>This is duplicated from WiX's Common class.</remarks>
565 + private static string GetIdentifierFromName(string name)
566 + {
567 + string result = IllegalIdentifierCharacters.Replace(name, "_"); // replace illegal characters with "_".
568
656 - /// <summary>
657 - /// QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout.
658 - /// </summary>
659 - QtExecCmdTimeoutAmbiguous,
569 + // MSI identifiers must begin with an alphabetic character or an
570 + // underscore. Prefix all other values with an underscore.
571 + if (AddPrefix.IsMatch(name))
572 + {
573 + result = String.Concat("_", result);
574 + }
575
661 - /// <summary>
662 - /// Directory/@ShortName may only be specified with Directory/@Name.
663 - /// </summary>
664 - AssignDirectoryNameFromShortName,
576 + return result;
577 + }
578
579 /// <summary>
667 - /// BootstrapperApplicationData attribute is deprecated and replaced with Unreal.
580 + /// Converter test types. These are used to condition error messages down to warnings.
581 /// </summary>
669 - BootstrapperApplicationDataDeprecated,
582 + private enum ConverterTestType
583 + {
584 + /// <summary>
585 + /// Internal-only: displayed when a string cannot be converted to an ConverterTestType.
586 + /// </summary>
587 + ConverterTestTypeUnknown,
588 +
589 + /// <summary>
590 + /// Displayed when an XML loading exception has occurred.
591 + /// </summary>
592 + XmlException,
593 +
594 + /// <summary>
595 + /// Displayed when a file cannot be accessed; typically when trying to save back a fixed file.
596 + /// </summary>
597 + UnauthorizedAccessException,
598 +
599 + /// <summary>
600 + /// Displayed when the encoding attribute in the XML declaration is not 'UTF-8'.
601 + /// </summary>
602 + DeclarationEncodingWrong,
603 +
604 + /// <summary>
605 + /// Displayed when the XML declaration is missing from the source file.
606 + /// </summary>
607 + DeclarationMissing,
608 +
609 + /// <summary>
610 + /// Displayed when the whitespace preceding a CDATA node is wrong.
611 + /// </summary>
612 + WhitespacePrecedingCDATAWrong,
613 +
614 + /// <summary>
615 + /// Displayed when the whitespace preceding a node is wrong.
616 + /// </summary>
617 + WhitespacePrecedingNodeWrong,
618 +
619 + /// <summary>
620 + /// Displayed when an element is not empty as it should be.
621 + /// </summary>
622 + NotEmptyElement,
623 +
624 + /// <summary>
625 + /// Displayed when the whitespace following a CDATA node is wrong.
626 + /// </summary>
627 + WhitespaceFollowingCDATAWrong,
628 +
629 + /// <summary>
630 + /// Displayed when the whitespace preceding an end element is wrong.
631 + /// </summary>
632 + WhitespacePrecedingEndElementWrong,
633 +
634 + /// <summary>
635 + /// Displayed when the xmlns attribute is missing from the document element.
636 + /// </summary>
637 + XmlnsMissing,
638 +
639 + /// <summary>
640 + /// Displayed when the xmlns attribute on the document element is wrong.
641 + /// </summary>
642 + XmlnsValueWrong,
643 +
644 + /// <summary>
645 + /// Assign an identifier to a File element when on Id attribute is specified.
646 + /// </summary>
647 + AssignAnonymousFileId,
648 +
649 + /// <summary>
650 + /// SuppressSignatureValidation attribute is deprecated and replaced with EnableSignatureValidation.
651 + /// </summary>
652 + SuppressSignatureValidationDeprecated,
653 +
654 + /// <summary>
655 + /// WixCA Binary/@Id has been renamed to UtilCA.
656 + /// </summary>
657 + WixCABinaryIdRenamed,
658 +
659 + /// <summary>
660 + /// QtExec custom actions have been renamed.
661 + /// </summary>
662 + QuietExecCustomActionsRenamed,
663 +
664 + /// <summary>
665 + /// QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout.
666 + /// </summary>
667 + QtExecCmdTimeoutAmbiguous,
668 +
669 + /// <summary>
670 + /// Directory/@ShortName may only be specified with Directory/@Name.
671 + /// </summary>
672 + AssignDirectoryNameFromShortName,
673 +
674 + /// <summary>
675 + /// BootstrapperApplicationData attribute is deprecated and replaced with Unreal.
676 + /// </summary>
677 + BootstrapperApplicationDataDeprecated,
678 + }
679 }
680 }
672 -}
src/test/WixToolsetTest.Converters/ConverterFixture.cs
+34
@@ -590,6 +590,40 @@ namespace WixToolsetTest.Converters
590 Assert.Equal(expected, actual);
591 }
592
593 + [Fact]
594 + public void CanConvertCustomAction()
595 + {
596 + var parse = String.Join(Environment.NewLine,
597 + "<?xml version='1.0' encoding='utf-8'?>",
598 + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
599 + " <CustomAction Id='Foo' BinaryKey='WixCA' DllEntry='CAQuietExec' />",
600 + " <CustomAction Id='Foo' BinaryKey='WixCA_x64' DllEntry='CAQuietExec64' />",
601 + " <CustomAction Id='Foo' BinaryKey='UtilCA' DllEntry='WixQuietExec' />",
602 + " <CustomAction Id='Foo' BinaryKey='UtilCA_x64' DllEntry='WixQuietExec64' />",
603 + "</Wix>");
604 +
605 + var expected = String.Join(Environment.NewLine,
606 + "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
607 + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
608 + " <CustomAction Id=\"Foo\" BinaryKey=\"Wix4UtilCA_X86\" DllEntry=\"WixQuietExec\" />",
609 + " <CustomAction Id=\"Foo\" BinaryKey=\"Wix4UtilCA_X64\" DllEntry=\"WixQuietExec64\" />",
610 + " <CustomAction Id=\"Foo\" BinaryKey=\"Wix4UtilCA_X86\" DllEntry=\"WixQuietExec\" />",
611 + " <CustomAction Id=\"Foo\" BinaryKey=\"Wix4UtilCA_X64\" DllEntry=\"WixQuietExec64\" />",
612 + "</Wix>");
613 +
614 + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
615 +
616 + var messaging = new DummyMessaging();
617 + var converter = new Wix3Converter(messaging, 2, null, null);
618 +
619 + var errors = converter.ConvertDocument(document);
620 +
621 + var actual = UnformattedDocumentString(document);
622 +
623 + Assert.Equal(6, errors);
624 + Assert.Equal(expected, actual);
625 + }
626 +
627 private static string UnformattedDocumentString(XDocument document)
628 {
629 var sb = new StringBuilder();