Escape license text embedded in the generated code - #47
Draft
yutailang0119 wants to merge 1 commit into
Draft
Conversation
yutailang0119
marked this pull request as draft
August 18, 2026 12:35
yutailang0119
force-pushed
the
fix/escape-generated-license-text
branch
from
August 18, 2026 12:40
4b90afd to
963fe05
Compare
The generated `Licenses+Generated.swift` embedded each LICENSE body into a plain multiline string literal, so a license text containing a backslash or a triple quote produced a file that does not compile. The failure surfaces while compiling the *consuming* target, which makes it hard to attribute to this plugin. Emit a raw string literal instead, and widen the `#` delimiter when the license text itself contains a sequence that would otherwise close it. A single `#` is not enough on its own: a raw literal delimited by one `#` still ends at `"""#` and still interprets `\#` as an escape. Add `PluginTests/TrickyLicense`, a local fixture package whose LICENSE contains those constructs, and assert that the generated `licenseText` reproduces it verbatim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
yutailang0119
force-pushed
the
fix/escape-generated-license-text
branch
from
August 18, 2026 14:59
963fe05 to
cdec71c
Compare
yutailang0119
marked this pull request as ready for review
August 18, 2026 15:34
yutailang0119
marked this pull request as draft
August 18, 2026 15:47
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Plugins/LicensesPlugin/LicensesPlugin.swiftembeds each dependency's LICENSE body into a plain Swift multiline string literal:licenseTextis never escaped, so a dependency whose LICENSE contains any of the following makes the generatedLicenses+Generated.swiftfail to compile — or, worse, silently alters the license text:\+ an invalid escape character (e.g.\q)error: invalid escape sequence in literal\n,\t,\0, …\(…)"""error: multi-line string literal closing delimiter must begin on a new lineBackslashes are not hypothetical in license files: Windows paths, TeX/roff fragments, and quoted shell examples all appear in real LICENSE files, and BSD-style licenses sometimes embed quoted blocks.
What makes this particularly awkward is where the error lands. The generated file is compiled as part of the consuming target, so the user sees errors pointing into
Nothing in that message names this plugin, and the file is inside
.build, so it looks like a corrupted build directory rather than a code-generation bug.Fix
Emit a raw string literal, and size the
#delimiter to the content:A single
#is not sufficient on its own, which is why the count is computed rather than hardcoded. A raw literal delimited by one#:"""#, and\#as an escape introducer — so\#nin a LICENSE would still become a real newline.rawDelimiterHashCount(for:)finds the longest run of#that follows either"""or\anywhere in the text and returns one more than that (minimum 1). Every real license inPluginTests/ExamplePackageneeds exactly one#; only the new fixture forces two.The shape of the generated file is otherwise unchanged — the closing delimiter still sits at column 0, so multiline-literal indentation stripping still does not apply, and license bodies remain readable in the generated output.
Alternative I considered
Emitting an escaped single-line literal would sidestep delimiter sizing entirely and be trivially correct, but it collapses each license onto one very long line (Apache-2.0 is ~11 KB), which makes the generated file unpleasant to read when debugging. I kept the readable multiline output, but I am happy to switch if you would prefer the simpler generator.
Tests
Added
PluginTests/TrickyLicense, a local fixture package referenced fromExamplePackagevia.package(path:). Its LICENSE deliberately contains\,\n,\q,\(,\#n,"""(both on its own line and mid-line) and"""#.ExamplePackageTests:TrickyLicenseto the existing exact license-name list (now 29 entries),licenseTextreproduces the fixture file byte-for-byte.The regression test is load-bearing — I verified both failure modes:
LicensesPlugin.swiftmakesswift buildinPluginTests/ExamplePackagefail withinvalid escape sequence in literalandmulti-line string literal content must begin on a new line;#also fails, on\#(and"""#— this is what the computed count prevents.Scope
Deliberately limited to the escaping fix and its regression test. Two things I noticed but left alone, to keep this reviewable:
idandnameare also interpolated into plain"…"literals without escaping. Package identities and display names cannot realistically contain"or\, so I did not touch them.Path/pluginWorkDirectorydeprecation warnings. Unrelated to this change; happy to send that separately.