Любой способ иметь структуру многострочного файла в FAKE?

В FAKE у вас обычно есть скрипт вроде:

// "foo.fsx"
#r @"./packages/tools/FAKE/tools/FakeLib.dll"

open Fake

Target "Foo" (fun _ -> trace "Here I am, foo the size of a bar" )

Run "Foo"

Что отлично работает. Теперь предположим, что вам нужен еще один buildscript, называемый "bar.fsx",

// "bar.fsx"
#r @"./packages/tools/FAKE/tools/FakeLib.dll"
#load "foo.fsx"

open Fake

Target "Bar" (fun _ -> trace "And you have me building software ..." )

Run "Bar"

Теперь это неправильно. Если вы попытаетесь запустить "bar.fsx", он сначала выполнит задачу "Foo", хотя я явно не просил об этом в качестве зависимости. Это потому, что директива load запускает foo.fsx script.

Итак, мой вопрос: как я могу импортировать файл foo.fsx, получить все целевые объекты, но не выполнять команду Run. Или, может быть, более интересно, и что я действительно хочу сделать; как я могу поделиться Target между сценариями в FAKE?

Это не помогает, если я помещаю определения Target в модуль, например:

module FooTargets =
    Target "Foo" (fun _ -> trace "Here I am, foo the size of a bar" )

что, возможно, слегка удивительно для меня...

Оцените любые советы о том, как это сделать. Моя "лучшая" идея до сих пор заключалась в определении некоторой константы в say 'foo.fsx', а затем условно load во всех других скриптах. Это кажется довольно оскорбительным, хотя (редактировать: и счастливо, даже не возможно.)

- Обновление: Ниже приведен более полный (то есть исполняемый) пример. Он не работает с FAKE 2.2.12.0 с ошибкой Duplicate Key, так как модуль Common был загружен дважды, и FAKE запустил целевой словарь с тем же целевым значением "Env".

// "common.fsx"
#r     @"../../tools/FAKE/FakeLib.dll"

open Fake

module Common = 
    Target "Env" (fun _ -> trace "Env")

// "foo.fsx"
#r     @"../../tools/FAKE/FakeLib.dll"
#load  @"common.fsx"

open Fake

module Foo =
    Target "Foo" (fun _ -> trace "Here I am, foo the size of a bar" )

"Env" ==> "Foo"

// "bar.fsx"
#r     @"../../tools/FAKE/FakeLib.dll"
#load  @"common.fsx"

open Fake

module Bar =
    Target "Bar" (fun _ -> trace "And you have me building software ..."  )

"Env" ==> "Bar"

// "marvin.fsx"
#r     @"../../tools/FAKE/FakeLib.dll"

#load  @"common.fsx"
#load  @"foo.fsx"
#load  @"bar.fsx"

open Fake

module Marvin =
    Target "Marvin" (fun _ -> ())

"Bar" ==> "Marvin"
"Foo" ==> "Marvin"

Run "Marvin"

Создайте с помощью fake.exe Marvin.fsx.

Ошибка:

System.TypeInitializationException: The type initializer for '<StartupCode$FSI_0002>.$FSI_0002_Common$fsx' threw an exception. ---> System.ArgumentException: An item with the same key has already been added.
   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at Fake.TargetHelper.targetFromTemplate[a](TargetTemplate`1 template, String name, a parameters)
   at [email protected](String name, a parameters)
   at <StartupCode$FSI_0002>.$FSI_0002_Common$fsx..cctor() in c:\Users\Noon\dev\beep\common.fsx:line 7
   --- End of inner exception stack trace ---
   at <StartupCode$FSI_0002>[email protected]() in c:\Users\Noon\dev\beep\foo.fsx:line 8
Stopped due to error
Searching for process with name = fsi.exe
Searching for process with name = msbuild

Ответ 1

Вот решение, которое я придумал. Я должен был выделить цели из процесса сборки из-за реалий #load и процесса FAKE для запуска целей. Но это по крайней мере достигает моей цели. Я не совсем уверен, как я к этому отношусь, из-за "свободной" связи между зависимостями между файлами; но, возможно, можно утверждать, что это хорошо.

Ответ 2

В Build.Tools мы положили код для различных целей в разные файлы script как обычные функции F #, а затем составляли цели вместе в Core.fsx, который устанавливает цели и их зависимости.

Одна из вещей в моем низкоприоритетном списке todo состоит в том, чтобы разделить Core на два файла - один, который формирует конфигурации и целевые определения, и тот, который устанавливает зависимости и вызовы Run. Таким образом, вы можете повторно использовать все базовые объекты при определении разных участников, которым не нужно было включать полное дерево зависимостей по умолчанию.

Текущий Core.fsx выглядит следующим образом:

#r    "./fake/fakelib.dll"
#load "./Utils.fsx"
#load "./Packaging.fsx"
#load "./Versioning.fsx"
#load "./Solution.fsx"
#load "./Test.fsx"
#load "./Specflow.fsx"

open System.IO
open Fake

let config = 
    Map.ofList [
        "build:configuration", environVarOrDefault "configuration"         "Release"
        "build:solution",      environVar          "solution"
        "core:tools",          environVar          "tools"
        "packaging:output",    environVarOrDefault "output"                (sprintf "%s\output" (Path.GetFullPath(".")))
        "packaging:updateid",  environVarOrDefault "updateid"              ""
        "packaging:pushurl",   environVarOrDefault "pushurl"               ""
        "packaging:apikey",    environVarOrDefault "apikey"                ""
        "packaging:packages",  environVarOrDefault "packages"              ""
        "versioning:build",    environVarOrDefault "build_number"          "0"
        "versioning:branch",   match environVar "teamcity_build_branch" with
                                   | "<default>" -> environVar "vcsroot_branch"
                                   | _ -> environVar "teamcity_build_branch"
    ]

Target "Default"           <| DoNothing
Target "Packaging:Package" <| Packaging.package config
Target "Packaging:Restore" <| Packaging.restore config
Target "Packaging:Update"  <| Packaging.update config
Target "Packaging:Push"    <| Packaging.push config
Target "Solution:Build"    <| Solution.build config
Target "Solution:Clean"    <| Solution.clean config
Target "Versioning:Update" <| Versioning.update config
Target "Test:Run"          <| Test.run config
Target "SpecFlow:Run"      <| Specflow.run config

"Solution:Clean"
    ==> "Packaging:Restore"
    ==> "Versioning:Update"
    ==> "Solution:Build"
    ==> "Packaging:Package"
    ==> "SpecFlow:Run"
    ==> "Test:Run"
    =?> ("Packaging:Push", not isLocalBuild)
    ==> "Default"

RunParameterTargetOrDefault "target" "Default"