如何让TemplateHelper处理UTF-8 BOM,EOL约定和读/执行权限?

时间:2014-12-05 01:35:50

标签: f# f#-fake

我正在尝试做什么

我正在尝试使用FAKE的TemplateHelper来实例化一些成为shell脚本(Unix和DOS)的模板文件。我在这样的FAKE任务中这样做:

CopyFile (buildApp @@ product + ".sh") (srcTem @@ "mono-launcher.sh")
processTemplates substs [(buildApp @@ product + ".sh")]

其中substs是替换的var->值映射,srcTembuildApp分别是模板源目录和构建输出目录。

什么是错的

我遇到了3个问题:

首先,TemplateHelper.processTemplates在生成的脚本的前面写了一个UTF-8 BOM。至少在Unix上,这会弄乱shebang行并在脚本运行时给出错误消息。原始模板的第一行的八进制转储和生成的文件显示这三个麻烦的字节的外观:

$ head -1 ./src/templates/mono-launcher.sh | od -c
0000000    #   !   /   b   i   n   /   s   h  \n
$ head -1 ./build/app/Hello-Fsharp.sh | od -c
0000000  357 273 277   #   !   /   b   i   n   /   s   h  \n

其次,没有办法说生成的Unix sh脚本中应该包含Unix EOL,而Windows .bat脚本应该有DOS EOL。

第三,没有明显的方法可以说生成的文件应具有世界读取和执行权限。

这个问题是关于是否有人知道如何处理这些问题,至少以比我在下面提出的方式更好的方式。

我在TemplateHelper来源和周围进行了一些探索。我发现了processTemplate次调用WriteFile调用了WriteToFileencoding使用EnvironmentHelper中定义的值为System.Text.Encoding.Default ASCIIEncodingTemplateHelper在我的系统上为UTF- 8。这已经足够了,但没有明显的方法来传递自定义编码,例如/// <summary>Copy a template file from source to destination, making string substitutions /// along the way and allowing for different file encodings at each end.</summary> /// /// <remarks>This only exists because TemplateHelper put UTF-8 BOM at the front, breaking /// script shebang lines. The EOL choice here works from Unix but needs testing on /// Windows (will it double up on returns if the build machine is Window?), and the /// read/execute permission setting is iffy. </remarks> /// /// <param name="substs">IDictionary of key, value string replacements to make</param> /// <param name="fromEncoding">Encoding of fromTemplate</param> /// <param name="fromTemplate">file with the source template</param> /// <param name="toEncoding">Encoding of toFile</param> /// <param name="toFile">file to which the instantiated template gets written</param> /// <param name="readExecute">whether to mark toFile as readable &amp; executable by all</param> /// /// <returns>Nothing of interest.</returns> let processTemplate substs fromEncoding fromTemplate toEncoding toFile unixEOL readExecute = let ReadTemplate encoding fileName = // Read lines of template file, but lazily: File.ReadLines(fileName, encoding) // start processing before entirety is read let SubstituteVariables substs = // Substitute variables in seq of line strs Seq.map (fun (line : string) -> // Loop over lines substs // Fold in effect of each key, val pair |> Seq.fold (fun (sb : StringBuilder) (KeyValue(k : string, v : string)) -> sb.Replace(k, v)) // Generates new StringBuilder w/each var (new StringBuilder(line)) // Initial accumulator is original line |> toText) // Convert final StringBuilder back to text let MaybeAddReturn = // Add extra return if not Unix EOLs Seq.map (fun line -> if unixEOL then line else line + "\r") // *** Test if works on Windows? let WriteTemplate encoding fileName (lines : seq<string>) = File.WriteAllLines(fileName, lines, encoding) // Write out altered lines (eol chosen how?) fileName // Return filename for next step in pipeline let MaybeSetReadExecute fileName = // Platform neutral, mark read + exec perms if readExecute then // Only do this if asked if isMono then // We're running on Mono Shell.Exec("chmod", "a+rx " + fileName, here) |> ignore // *** Horrible kludge? else // Not under Mono: do it the .Net way // *** Equiv kludge: Shell.Exec("icacls", fileName + " /grant S-1-1-0:RX", here) |> ignore let fs = File.GetAccessControl(fileName) // Mac OSX: System.NotSupportedException fs.AddAccessRule(new FileSystemAccessRule( // new SecurityIdentifier(WellKnownSidType.WorldSid, null), FileSystemRights.ReadAndExecute, AccessControlType.Allow)) // *** Seems pretty complex for such a simple File.SetAccessControl(fileName, fs) // task as just setting rx permissions! let shortClassName obj = // Just class name, no namespacey qualifiers let str = obj.ToString() in str.Substring(str.LastIndexOf('.') + 1) logfn " Template %s (%s) generating %s (%s, %s EOLs%s)." fromTemplate (shortClassName fromEncoding) // Where we're coming from, toFile (shortClassName toEncoding) // where we're going, (if unixEOL then "Unix" else "DOS") // and in what condition we propose to (if readExecute then ", read/execute" else "") // arrive there fromTemplate // Start with the input template filename |> ReadTemplate fromEncoding // read it in lazily (enumeration of lines) |> SubstituteVariables substs // substitute variables in each line |> MaybeAddReturn // fiddle with EOLs for target platform |> WriteTemplate toEncoding toFile // write out in destination encoding |> MaybeSetReadExecute // finally set permissions if asked

我做了什么

所以......如果没有任何方法说服TemplateHelper做我想做的事,我用以下内容代替它:

Target "BuildLauncherScripts" (fun _ ->                // Scripts really only needed for mono
  let asciiEncoding = new ASCIIEncoding()              // Avoid UTF-8 BOM which breaks shebang lines
  let generateAScript (fromTempl : String) =           // Instantiate a script from template file
    let ext = fromTempl.Substring(fromTempl.LastIndexOf('.'))
    processTemplate projProps                          //   projProps to substitute template vars
      asciiEncoding (srcTem   @@ fromTempl)            //   copy from templates to build area
      asciiEncoding (buildApp @@ (projProps.Item("@product")) + ext)
      (ext = ".sh") true                               //   always executable, because it's a script
  generateAScript "mono-launcher.sh"                   // Unix script to launch mono + application
  generateAScript "bat-launcher.bat"                   // Windows script to just launch application
)                                                      // 

它一次处理一个文件,而不是像TemplateHelper这样的序列处理文件,因为它太毛了,不能提供并行的读取和读取序列。写编码。但作为奖励,它消除了我在源中发现的可变状态的使用,使其更具功能性,如果有人关心的话。增加的功能是提供输入和输出编码,处理EOL以及可选地为生成的文件设置读/执行权限的能力。

以下是使用中的示例:

MaybeSetReadExecute

我想听听你的意见

  1. 有没有办法让{{1}}来处理输入/输出文件编码,EOL约定以及设置读/执行权限?
  2. 如果没有,上述是否是一种合理的处理方式?
  3. 特别是,EOL代码是否可能在Windows上运行,还是会在返回时加倍?
  4. 此外,是否有某种方式以平台中立的方式设置读/执行权限? (我在{{1}}上面的代码是一个令人恐惧的混合物,包括炮轰和模仿我发现的一些.NET代码但很难理解。)

0 个答案:

没有答案