├── tests
├── datas
│ ├── TestProject
│ │ ├── test.txt
│ │ ├── Test2.fs
│ │ ├── Content.html
│ │ ├── TestProject.fsproj
│ │ └── Test.fs
│ ├── TestLib2
│ │ ├── Library2.fs
│ │ ├── Added.fs
│ │ ├── Library.fs
│ │ └── TestLib2.fsproj
│ ├── TestLib1
│ │ ├── Library.fs
│ │ └── TestLib1.fsproj
│ └── repro-projects
│ │ └── src
│ │ ├── Masse.Logging
│ │ └── Masse.Logging.fsproj
│ │ ├── Masse.AWS.SQS
│ │ └── Masse.AWS.SQS.fsproj
│ │ ├── Masse.AWS.SecretManager
│ │ └── Masse.AWS.SecretManager.fsproj
│ │ ├── Masse.Event
│ │ └── Masse.Event.fsproj
│ │ ├── Masse.Swagger
│ │ └── Masse.Swagger.fsproj
│ │ ├── Masse.Redis
│ │ └── Masse.Redis.fsproj
│ │ ├── Masse.AWS.S3
│ │ └── Masse.AWS.S3.fsproj
│ │ ├── src.sln
│ │ ├── Masse.Entity.Decorator
│ │ └── Masse.Entity.Decorator.fsproj
│ │ ├── Masse.AWS.ECS
│ │ └── Masse.AWS.ECS.fsproj
│ │ ├── Masse.Sql
│ │ └── Masse.Sql.fsproj
│ │ ├── ScratchpadNet45
│ │ └── ScratchpadNet45.fsproj
│ │ ├── Masse.Telemetry
│ │ └── Masse.Telemetry.fsproj
│ │ ├── Masse.Catalog.Batch
│ │ └── Masse.Catalog.Batch.fsproj
│ │ ├── Masse.Image
│ │ └── Masse.Image.fsproj
│ │ ├── Masse.AWS.DynamoDb
│ │ └── Masse.AWS.DynamoDb.fsproj
│ │ ├── Masse.Common.JsonNet
│ │ └── Masse.Common.JsonNet.fsproj
│ │ ├── Masse.Analytics.Sql
│ │ └── Masse.Analytics.Sql.fsproj
│ │ ├── Masse.Test
│ │ └── Masse.Test.fsproj
│ │ ├── Masse.AWS.CloudSearch
│ │ └── Masse.AWS.CloudSearch.fsproj
│ │ ├── Masse.AWS.Ecommerce
│ │ └── Masse.AWS.Ecommerce.fsproj
│ │ ├── Masse.API.Image
│ │ └── Masse.API.Image.fsproj
│ │ ├── Masse.Entity
│ │ └── Masse.Entity.fsproj
│ │ ├── Masse.Social.Journal
│ │ └── Masse.Social.Journal.fsproj
│ │ ├── Masse.Source
│ │ └── Masse.Source
│ │ │ └── Masse.Source.fsproj
│ │ ├── Masse.Ping
│ │ └── Masse.Ping.fsproj
│ │ ├── Masse.GraphDb
│ │ └── Masse.GraphDb.fsproj
│ │ ├── Masse.Source.Walmart
│ │ └── Masse.Source.Walmart.fsproj
│ │ ├── Masse.Source.Farfetch
│ │ └── Masse.Source.Farfetch.fsproj
│ │ ├── Masse.Source.Maisonette
│ │ └── Masse.Source.Maisonette.fsproj
│ │ ├── Masse.Catalog.Ingest
│ │ └── Masse.Catalog.Ingest.fsproj
│ │ ├── Masse.Catalog.Sql
│ │ └── Masse.Catalog.Sql.fsproj
│ │ ├── Masse.Product.Journal
│ │ └── Masse.Product.Journal.fsproj
│ │ ├── Masse.Source.Everlane
│ │ └── Masse.Source.Everlane.fsproj
│ │ ├── Masse.Source.Glossier
│ │ └── Masse.Source.Glossier.fsproj
│ │ ├── Masse.Common
│ │ └── Masse.Common.fsproj
│ │ ├── Masse.Source.Viglink
│ │ └── Masse.Source.Viglink.fsproj
│ │ ├── Masse.Source.ShopStyle
│ │ └── Masse.Source.ShopStyle.fsproj
│ │ ├── Masse.Trigger
│ │ └── Masse.Trigger.fsproj
│ │ ├── Masse.Source.External
│ │ └── Masse.Source.External.fsproj
│ │ ├── Masse.Source.TheTot
│ │ └── Masse.Source.TheTot.fsproj
│ │ ├── Masse.Catalog.Search
│ │ └── Masse.Catalog.Search.fsproj
│ │ ├── Masse.Source.Rakuten
│ │ └── Masse.Source.Rakuten.fsproj
│ │ ├── Masse.Catalog.Indexer
│ │ └── Masse.Catalog.Indexer.fsproj
│ │ ├── Masse.Social.Indexer
│ │ └── Masse.Social.Indexer.fsproj
│ │ ├── Masse.Source.Download
│ │ └── Masse.Source.Download.fsproj
│ │ ├── Masse.Social.Sql
│ │ └── Masse.Social.Sql.fsproj
│ │ ├── Masse.Admin.Task
│ │ └── Masse.Admin.Task.fsproj
│ │ ├── Scratchpad
│ │ └── Scratchpad.fsproj
│ │ ├── Masse.Social.Search
│ │ └── Masse.Social.Search.fsproj
│ │ ├── Masse.SiftScience
│ │ └── Masse.SiftScience.fsproj
│ │ ├── Masse.GraphQl
│ │ └── Masse.GraphQl.fsproj
│ │ ├── Masse.Catalog.Journal
│ │ └── Masse.Catalog.Journal.fsproj
│ │ ├── Masse.API
│ │ └── Masse.API.fsproj
│ │ └── Masse.Admin.Tools
│ │ └── Masse.Admin.Tools.fsproj
├── FcsWatch.Tests
│ ├── paket.references
│ ├── Program.fs
│ ├── FcsWatch.Tests.fsproj
│ ├── Types.fs
│ └── Tests.fs
├── FcsWatch.InteractionTests
│ ├── paket.references
│ ├── Program.fs
│ ├── FcsWatch.InteractionTests.fsproj
│ ├── InteractionTests.fs
│ └── Types.fs
└── FsLive.Porta.Cli.Tests
│ └── FsLive.Porta.Cli.Tests.fsproj
├── .paket
├── paket.exe
└── paket.targets
├── src
├── FcsWatch.Core
│ ├── paket.references
│ ├── FcsWatch.Core.fsproj
│ ├── Logger.fs
│ ├── Compiler.fs
│ └── CompilerTmpEmitter.fs
├── fcswatch-cli
│ ├── Properties
│ │ └── launchSettings.json
│ ├── fcswatch-cli.fsproj
│ ├── Program.fs
│ └── Share.fs
├── FcsWatch.Binary.Helpers
│ ├── FcsWatch.Binary.Helpers.csproj
│ └── PublicExtensions.cs
├── FcsWatch.Porta.Interpreter
│ ├── FcsWatch.Porta.Interpreter.fsproj
│ └── CodeModel.fs
├── FcsWatch.Porta
│ ├── FcsWatch.Porta.fsproj
│ ├── Main.fs
│ ├── CompilerTmpEmitter.fs
│ └── Extensions.fs
├── fslive-cli
│ ├── fslive-cli.fsproj
│ └── fslive.fs
└── FcsWatch.Binary
│ ├── FcsWatch.Binary.fsproj
│ ├── VsCodeHelper.fs
│ ├── Extensions.fs
│ ├── Main.fs
│ ├── DebuggingServer.fs
│ └── AutoReload.fs
├── .appveyor.yml
├── paket.dependencies
├── .vscode
├── tasks.json
├── settings.json
└── launch.json
├── .circleci
└── config.yml
├── LICENSE
├── RELEASE_NOTES.md
├── .gitignore
├── README.md
└── FCSWatch.FPublisher.sln
/tests/datas/TestProject/test.txt:
--------------------------------------------------------------------------------
1 |
2 | FCSWatch from embedded resource!
--------------------------------------------------------------------------------
/.paket/paket.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humhei/FCSWatch/HEAD/.paket/paket.exe
--------------------------------------------------------------------------------
/tests/datas/TestLib2/Library2.fs:
--------------------------------------------------------------------------------
1 | module Library2
2 | let test = " "
--------------------------------------------------------------------------------
/tests/datas/TestProject/Test2.fs:
--------------------------------------------------------------------------------
1 | module FS
2 | let ss = "pda d 99sa "
--------------------------------------------------------------------------------
/tests/FcsWatch.Tests/paket.references:
--------------------------------------------------------------------------------
1 | Expecto
2 | Dotnet.ProjInfo
3 | Fake.DotNet.Cli
4 | FSharp.Compiler.Service
--------------------------------------------------------------------------------
/tests/datas/TestProject/Content.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/tests/FcsWatch.InteractionTests/paket.references:
--------------------------------------------------------------------------------
1 | Expecto
2 | Dotnet.ProjInfo
3 | Fake.DotNet.Cli
4 | FSharp.Compiler.Service
--------------------------------------------------------------------------------
/src/FcsWatch.Core/paket.references:
--------------------------------------------------------------------------------
1 | Dotnet.ProjInfo
2 | Fake.DotNet.Cli
3 | FSharp.Compiler.Service
4 | Dotnet.ProjInfo.Workspace
--------------------------------------------------------------------------------
/tests/datas/TestLib2/Added.fs:
--------------------------------------------------------------------------------
1 | namespace TestLib2
2 |
3 | module Added =
4 | let hello2 name =
5 | printfn "Hello %s" name
6 |
--------------------------------------------------------------------------------
/tests/datas/TestLib1/Library.fs:
--------------------------------------------------------------------------------
1 | namespace TestLib1
2 |
3 | module Say =
4 | let hello11 name =
5 | printfn "He l %s" name
6 |
7 |
--------------------------------------------------------------------------------
/tests/datas/TestLib2/Library.fs:
--------------------------------------------------------------------------------
1 | namespace TestLib2
2 |
3 | module Say =
4 | let hello2 name =
5 | printfn "He %s " name
6 |
7 | let fromLib2 = "das ssss"
--------------------------------------------------------------------------------
/src/fcswatch-cli/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "fcswatch-cli": {
4 | "commandName": "Project",
5 | "commandLineArgs": "--project-file \"D:\\VsCode\\Workspace\\FCSWatch\\tests\\datas\\TestProject\\TestProject.fsproj\""
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/.appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2017
2 |
3 | install:
4 | - dotnet tool install --global fpublisher-cli
5 |
6 | build_script:
7 | - cmd: fpublisher --run-ci
8 |
9 | environment:
10 | nuget_api_key:
11 | secure: uxeBhnDDyowaBY1oDNFPo9Yz9wbJUWwAw8BgagEbHtKLQR3uxOADA7dvO+pcANt0
12 |
--------------------------------------------------------------------------------
/paket.dependencies:
--------------------------------------------------------------------------------
1 | source https://api.nuget.org/v3/index.json
2 | storage: none
3 | nuget FSharp.Compiler.Service
4 | nuget Expecto
5 | nuget Dotnet.ProjInfo
6 | nuget Fake.DotNet.Cli ~> 5.13.2
7 | nuget Suave
8 | nuget Dotnet.ProjInfo.Workspace
9 | github aspnet/AspNetCore:316fbbe9c42477b6c1d2088b5ef72f8ee3462186 src/Shared/Process/ProcessExtensions.cs
10 |
--------------------------------------------------------------------------------
/src/FcsWatch.Binary.Helpers/FcsWatch.Binary.Helpers.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/FcsWatch.Binary.Helpers/PublicExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Text;
5 |
6 | namespace FcsWatch.Binary.Helpers
7 | {
8 | public static class PublicExtensions
9 | {
10 | public static void KillTree(this Process process) => Microsoft.Extensions.Internal.ProcessExtensions.KillTree(process);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Logging/Masse.Logging.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/tests/datas/TestLib2/TestLib2.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "emitCompilerTmp",
6 | "command": "curl",
7 | "group": "build",
8 | "args": [
9 | "--config",
10 | ".fake/fcswatch/port.cache"
11 | ],
12 | "presentation": {
13 | "reveal": "never"
14 | },
15 | "problemMatcher": []
16 | },
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | general:
3 | branches:
4 | ignore:
5 | - gh-pages # list of branches to ignore
6 |
7 | jobs:
8 | build:
9 | docker:
10 | - image: humhei/docker-dotnet-mono
11 | steps:
12 | - checkout
13 | - run:
14 | name: run-tests
15 | command: |
16 | export FrameworkPathOverride=$(dirname $(which mono))/../lib/mono/4.6.2-api/
17 | export TERM=xterm-256color
18 | fpublisher --run-ci
--------------------------------------------------------------------------------
/src/FcsWatch.Porta.Interpreter/FcsWatch.Porta.Interpreter.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.AWS.SQS/Masse.AWS.SQS.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/tests/FcsWatch.InteractionTests/Program.fs:
--------------------------------------------------------------------------------
1 | // Learn more about F# at http://fsharp.org
2 |
3 | open Expecto.Logging
4 | open Expecto.Tests
5 | open FcsWatch.InteractionTests.InteractionTests
6 |
7 | let testConfig =
8 | { Expecto.Tests.defaultConfig with
9 | parallelWorkers = 1
10 | verbosity = LogLevel.Debug }
11 |
12 | let tests =
13 | testList "All tests" [
14 | interactionTests
15 | ]
16 | // Create an interactive checker instance
17 | []
18 | let main argv = runTests testConfig tests
19 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.AWS.SecretManager/Masse.AWS.SecretManager.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Event/Masse.Event.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Swagger/Masse.Swagger.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Redis/Masse.Redis.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.AWS.S3/Masse.AWS.S3.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/src.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26124.0
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Any CPU = Debug|Any CPU
9 | Debug|x64 = Debug|x64
10 | Debug|x86 = Debug|x86
11 | Release|Any CPU = Release|Any CPU
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(SolutionProperties) = preSolution
16 | HideSolutionNode = FALSE
17 | EndGlobalSection
18 | EndGlobal
19 |
--------------------------------------------------------------------------------
/tests/FcsWatch.Tests/Program.fs:
--------------------------------------------------------------------------------
1 | // Learn more about F# at http://fsharp.org
2 |
3 | open Expecto.Logging
4 | open Expecto.Tests
5 | open FcsWatch.Tests.Tests
6 | open System
7 |
8 | let testConfig =
9 | { Expecto.Tests.defaultConfig with
10 | parallelWorkers = 1
11 | verbosity = LogLevel.Debug }
12 |
13 | let tests =
14 | testList "All tests" [
15 | programTests
16 | webhookTests
17 | pluginTests
18 | functionTests
19 | ]
20 | // Create an interactive checker instance
21 | []
22 | let main argv =
23 | runTests testConfig tests
24 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Entity.Decorator/Masse.Entity.Decorator.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/FcsWatch.Porta/FcsWatch.Porta.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/tests/datas/TestLib1/TestLib1.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0;net462
4 | true
5 |
6 |
7 |
8 | TestLib2.fsproj
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.AWS.ECS/Masse.AWS.ECS.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Sql/Masse.Sql.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/ScratchpadNet45/ScratchpadNet45.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net461
6 | portable
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Telemetry/Masse.Telemetry.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/tests/FcsWatch.Tests/FcsWatch.Tests.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.2
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Catalog.Batch/Masse.Catalog.Batch.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/fcswatch-cli/fcswatch-cli.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6
6 | fcswatch
7 | True
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Image/Masse.Image.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/FcsWatch.InteractionTests/FcsWatch.InteractionTests.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.2
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.AWS.DynamoDb/Masse.AWS.DynamoDb.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Common.JsonNet/Masse.Common.JsonNet.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Library
5 | netcoreapp2.1
6 | portable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Analytics.Sql/Masse.Analytics.Sql.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Test/Masse.Test.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.AWS.CloudSearch/Masse.AWS.CloudSearch.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | // Configure glob patterns for excluding files and folders.
4 | "files.exclude": {
5 | "**/.git": true,
6 | "**/.DS_Store": true,
7 | "**/.vs": true,
8 | "**/bin": true,
9 | "**/obj": true,
10 | "**/repro-projects": true,
11 | },
12 | "search.exclude": {
13 | "**/node_modules": true,
14 | "**/bower_components": true,
15 | "**/packages": true,
16 | "**/paket-files": true,
17 | "**/artifacts": true,
18 | "**/bundles.js": true,
19 | "**/bundles.js.map": true,
20 | "**/temp": true,
21 | "**/repro-projects": true,
22 | },
23 | "files.trimFinalNewlines": true,
24 | "files.trimTrailingWhitespace": true,
25 | "FSharp.disableFailedProjectNotifications": true
26 | }
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.AWS.Ecommerce/Masse.AWS.Ecommerce.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.API.Image/Masse.API.Image.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 | MasseAPIImage
7 | true
8 | Lambda
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Entity/Masse.Entity.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Social.Journal/Masse.Social.Journal.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Always
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source/Masse.Source/Masse.Source.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/fslive-cli/fslive-cli.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.2
6 | fslive_cli
7 | fslive
8 | True
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | fslive-cli-test
17 | .NET Core Global Tool for FsLive - allows to send files to Fabulous.LiveUpdate and more
18 | Tool;CLI
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Ping/Masse.Ping.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | netcoreapp2.1
5 | MassePing
6 | true
7 | Lambda
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.GraphDb/Masse.GraphDb.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source.Walmart/Masse.Source.Walmart.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source.Farfetch/Masse.Source.Farfetch.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source.Maisonette/Masse.Source.Maisonette.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/datas/TestProject/TestProject.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6;
5 | true
6 |
7 |
8 |
9 | TestLib.fsproj
10 |
11 |
12 |
13 |
14 | PreserveNewest
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Catalog.Ingest/Masse.Catalog.Ingest.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Always
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Catalog.Sql/Masse.Catalog.Sql.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Product.Journal/Masse.Product.Journal.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Always
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/tests/datas/TestProject/Test.fs:
--------------------------------------------------------------------------------
1 | // Learn more about F# at http://fsharp.org
2 |
3 | open System
4 | open System.Collections.Concurrent
5 | open System.Threading
6 | open TestLib2
7 | open System.IO
8 |
9 | /// https://github.com/humhei/FCSWatch/issues/23
10 |
11 | //let private getEmbeddedStringFromFile (resourceName:string) =
12 | // let assembly = System.Reflection.Assembly.GetExecutingAssembly()
13 | // printfn "Assembly: %A" assembly
14 | // let rns = assembly.GetManifestResourceNames()
15 | // printfn "Resource Names: %A" rns
16 | // let rn = assembly.GetManifestResourceNames() |> Seq.find (fun n -> n.EndsWith resourceName)
17 | // use s = assembly.GetManifestResourceStream rn
18 | // use sr = new StreamReader(s)
19 | // sr.ReadToEnd()
20 |
21 | //let private resource = getEmbeddedStringFromFile "test.txt"
22 |
23 | []
24 | let main _ =
25 |
26 | //printfn "Hell world from resource %s" resource
27 |
28 | printfn "HEsSSS 9108 985966911 91100099999ASASSASA"
29 |
30 | /// simulate server
31 | Console.ReadLine()
32 | |> ignore
33 |
34 | 0 // return an integer exit code
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 humhei
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Debug FcsWatch.Tests",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "program": "${workspaceFolder}/tests/FcsWatch.Tests/bin/Debug/netcoreapp2.2/FcsWatch.Tests.dll",
12 | },
13 | {
14 | "name": "Debug FcsWatch.InteractionTests",
15 | "type": "coreclr",
16 | "request": "launch",
17 | "program": "${workspaceFolder}/tests/FcsWatch.InteractionTests/bin/Debug/netcoreapp2.2/FcsWatch.InteractionTests.dll",
18 | },
19 | {
20 | "name": "launch TestProject",
21 | "type": "coreclr",
22 | "request": "launch",
23 | "preLaunchTask": "emitCompilerTmp",
24 | "program": "${workspaceFolder}/tests/datas/TestProject/bin/Debug/netcoreapp2.2/TestProject.dll",
25 | },
26 | ]
27 | }
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source.Everlane/Masse.Source.Everlane.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source.Glossier/Masse.Source.Glossier.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Common/Masse.Common.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Library
5 | netcoreapp2.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source.Viglink/Masse.Source.Viglink.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 | viglinkCatTree.txt
11 | PreserveNewest
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source.ShopStyle/Masse.Source.ShopStyle.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 | shopstyle_categories.json
11 | PreserveNewest
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tests/FsLive.Porta.Cli.Tests/FsLive.Porta.Cli.Tests.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.2
4 | Exe
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Trigger/Masse.Trigger.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 | Lambda
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source.External/Masse.Source.External.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/FcsWatch.Binary/FcsWatch.Binary.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | $(TargetsForTfmSpecificBuildOutput);IncludeProcessExtensions
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source.TheTot/Masse.Source.TheTot.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Catalog.Search/Masse.Catalog.Search.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 | MasseCatalogSearch
5 | Lambda
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/fcswatch-cli/Program.fs:
--------------------------------------------------------------------------------
1 | // Learn more about F# at http://fsharp.org
2 | module FcsWatch.Cli.Main
3 | open FcsWatch.Binary
4 | open FcsWatch.Cli.Share
5 | open System.Threading.Tasks
6 |
7 |
8 | let splitArgs args =
9 | let arguArgs =
10 | args
11 | |> Array.takeWhile(fun x -> x <> "--")
12 | let additionalArgs =
13 | args
14 | |> Array.skipWhile(fun x -> x <> "--")
15 | |> (fun a -> if Array.tryHead a = Some "--" then Array.tail a else a)
16 | (arguArgs, additionalArgs)
17 |
18 | []
19 | let main argv =
20 | try
21 | let exited = TaskCompletionSource()
22 | System.Console.CancelKeyPress.Add(fun _ ->
23 | exited.TrySetResult ()
24 | |> ignore
25 | )
26 |
27 | System.Runtime.Loader.AssemblyLoadContext.Default.add_Unloading(fun _ ->
28 | exited.TrySetResult ()
29 | |> ignore
30 | )
31 | let arguArgs, additionalArgs = splitArgs argv
32 |
33 | let results = parser.Parse arguArgs
34 |
35 | let processResult = processParseResults additionalArgs results
36 |
37 | runFcsWatcher exited.Task processResult.Config processResult.ProjectFile
38 | |> Async.RunSynchronously
39 |
40 | 0
41 |
42 | with :? Argu.ArguParseException as e ->
43 | stdout.WriteLine e.Message
44 |
45 | LanguagePrimitives.EnumToValue e.ErrorCode
46 |
--------------------------------------------------------------------------------
/src/FcsWatch.Core/FcsWatch.Core.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net472;netstandard2.0
5 |
6 |
7 | TRACE
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 6.0.0
24 |
25 |
26 | 6.0.0
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source.Rakuten/Masse.Source.Rakuten.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Catalog.Indexer/Masse.Catalog.Indexer.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 | MasseCatalogIndexer
5 | Lambda
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/tests/FcsWatch.InteractionTests/InteractionTests.fs:
--------------------------------------------------------------------------------
1 | module FcsWatch.InteractionTests.InteractionTests
2 | open Expecto
3 | open Fake.IO
4 | open Fake.IO.FileSystemOperators
5 |
6 | let pass() = Expect.isTrue true "passed"
7 | let fail() = Expect.isTrue false "failed"
8 |
9 | let root = __SOURCE_DIRECTORY__ > "../../"
10 |
11 | let datas = Path.getDirectory(__SOURCE_DIRECTORY__) > "datas"
12 |
13 | let entryProjDir = datas > "TestProject"
14 |
15 | let entryProjPath = entryProjDir > "TestProject.fsproj"
16 |
17 | let testProjPath = datas > @"TestLib2/TestLib2.fsproj"
18 |
19 | let testSourceFile1 = datas > @"TestLib2/Library.fs"
20 |
21 | let testSourceFile2 = datas > @"TestLib2/Library2.fs"
22 |
23 | let testSourceFileAdded = datas > @"TestLib2/Added.fs"
24 |
25 | let testSourceFile1InTestLib = datas > @"TestLib1/Library.fs"
26 |
27 |
28 | let interactionTests =
29 | testList "interaction tests" [
30 | testCase "manual reload test" <| fun _ ->
31 | FcsWatch.Cli.Main.main [|"--project-file"; entryProjPath;"--logger-level"; "normal"; "--debuggable" |]
32 | |> ignore
33 |
34 | ftestCase "auto reload test" <| fun _ ->
35 | FcsWatch.Cli.Main.main [|"--project-file"; entryProjPath|]
36 | |> ignore
37 |
38 | testCase "auto reload release" <| fun _ ->
39 | FcsWatch.Cli.Main.main [|"--project-file"; entryProjPath; "--configuration"; "release"|]
40 | |> ignore
41 |
42 | testCase "fslive cli test" <| fun _ ->
43 | FsLive.Driver.main [| entryProjPath; "--watch"; "--loggerlevel:2"; "--send"|]
44 | |> ignore
45 | ]
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Social.Indexer/Masse.Social.Indexer.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 | MasseSocialIndexer
5 | Lambda
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Source.Download/Masse.Source.Download.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Always
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Social.Sql/Masse.Social.Sql.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Admin.Task/Masse.Admin.Task.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 | MasseAdminTask
6 | Lambda
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Scratchpad/Scratchpad.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | netcoreapp2.1
5 |
6 |
7 |
8 | PreserveNewest
9 |
10 |
11 |
12 | PreserveNewest
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Social.Search/Masse.Social.Search.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 | MasseSocialSearch
7 | Lambda
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/tests/FcsWatch.Tests/Types.fs:
--------------------------------------------------------------------------------
1 | module FcsWatch.Tests.Types
2 | open System.Xml
3 |
4 | []
5 | module Fsproj =
6 | let private equalIgnoreCaseAndEdgeSpace (text1: string) (text2: string) =
7 | match text1.Trim().CompareTo (text2.Trim()) with
8 | | 0 -> true
9 | | _ -> false
10 |
11 | let addFileToProject file (projectFile: string) =
12 | let doc = new XmlDocument()
13 | doc.Load(projectFile)
14 | let compiledFiles = doc.GetElementsByTagName "Compile"
15 | let compiledFileList =
16 | [ for compiledFile in compiledFiles do
17 | yield compiledFile
18 | ]
19 | match List.tryFind (fun (compiledFile: XmlNode) ->
20 | equalIgnoreCaseAndEdgeSpace file compiledFile.Attributes.["Include"].Value) compiledFileList with
21 | | Some _ -> ()
22 | | None ->
23 | let firstCompiledFile = compiledFiles.[0]
24 | let addedCompiledFile = firstCompiledFile.Clone()
25 | addedCompiledFile.Attributes.["Include"].Value <- file
26 |
27 | firstCompiledFile.ParentNode.AppendChild addedCompiledFile
28 | |> ignore
29 |
30 | doc.Save(projectFile)
31 | let removeFileFromProject file (projectFile: string) =
32 | let doc = new XmlDocument()
33 | doc.Load(projectFile)
34 |
35 | let compiledFiles = doc.GetElementsByTagName "OutputType"
36 | let compiledFileList =
37 | [ for compiledFile in compiledFiles do
38 | yield compiledFile
39 | ]
40 | match List.tryFind (fun (compiledFile: XmlNode) ->
41 | equalIgnoreCaseAndEdgeSpace file compiledFile.Attributes.["Include"].Value) compiledFileList with
42 | | Some xmlNode ->
43 | xmlNode.ParentNode.RemoveChild xmlNode |> ignore
44 | | None -> ()
45 | doc.Save(projectFile)
46 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.SiftScience/Masse.SiftScience.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 | MasseSiftScience
7 | Lambda
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/tests/FcsWatch.InteractionTests/Types.fs:
--------------------------------------------------------------------------------
1 | module FcsWatch.InteractionTests.Types
2 | open System.Xml
3 |
4 | []
5 | module Fsproj =
6 | let private equalIgnoreCaseAndEdgeSpace (text1: string) (text2: string) =
7 | match text1.Trim().CompareTo (text2.Trim()) with
8 | | 0 -> true
9 | | _ -> false
10 |
11 | let addFileToProject file (projectFile: string) =
12 | let doc = new XmlDocument()
13 | doc.Load(projectFile)
14 | let compiledFiles = doc.GetElementsByTagName "Compile"
15 | let compiledFileList =
16 | [ for compiledFile in compiledFiles do
17 | yield compiledFile ]
18 | match List.tryFind (fun (compiledFile: XmlNode) ->
19 | equalIgnoreCaseAndEdgeSpace file compiledFile.Attributes.["Include"].Value) compiledFileList with
20 | | Some _ -> ()
21 | | None ->
22 | let firstCompiledFile = compiledFiles.[0]
23 | let addedCompiledFile = firstCompiledFile.Clone()
24 | addedCompiledFile.Attributes.["Include"].Value <- file
25 |
26 | firstCompiledFile.ParentNode.AppendChild addedCompiledFile
27 | |> ignore
28 |
29 | doc.Save(projectFile)
30 | let removeFileFromProject file (projectFile: string) =
31 | let doc = new XmlDocument()
32 | doc.Load(projectFile)
33 | let compiledFiles = doc.GetElementsByTagName "Compile"
34 | let compiledFileList =
35 | [ for compiledFile in compiledFiles do
36 | yield compiledFile
37 | ]
38 | match List.tryFind (fun (compiledFile: XmlNode) ->
39 | equalIgnoreCaseAndEdgeSpace file compiledFile.Attributes.["Include"].Value) compiledFileList with
40 | | Some xmlNode ->
41 | xmlNode.ParentNode.RemoveChild xmlNode |> ignore
42 | | None -> ()
43 | doc.Save(projectFile)
44 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.GraphQl/Masse.GraphQl.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/FcsWatch.Porta/Main.fs:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Fabulous contributors. See LICENSE.md for license.
2 |
3 | // F# PortaCode command processing (e.g. used by Fabulous.Cli)
4 |
5 | []
6 | module FcsWatch.Porta.Main
7 |
8 | open System
9 | open FSharp.Compiler.SourceCodeServices
10 | open FcsWatch.Core.Compiler
11 | open FcsWatch.Core
12 | open FcsWatch.Core.FcsWatcher
13 | open FcsWatch.Core.Types
14 | open Extensions
15 | open Fake.IO
16 | open System.IO
17 |
18 | let runFcsWatcher (config: PortaConfig) =
19 | logger <- Logger.create config.LoggerLevel
20 | let compiler =
21 | { new ICompiler with
22 | member x.Compile(checker, crackedFsproj) = async {
23 | let! result = CrackedFsproj.check config.UseEditFiles config.LiveCheckOnly checker crackedFsproj
24 | return [|result|]
25 | }
26 | member x.WarmCompile = true
27 | member x.Summary (_, _) = ""
28 | }
29 |
30 | let compilerTmpEmitter = CompilerTmpEmitter.create config
31 |
32 |
33 | let coreConfig: FcsWatch.Core.Config =
34 | { LoggerLevel = config.LoggerLevel
35 | WorkingDir = config.WorkingDir
36 | UseEditFiles = config.UseEditFiles
37 | OtherFlags = config.OtherFlags
38 | Configuration = Configuration.Debug }
39 |
40 | let checker = FSharpChecker.Create(keepAssemblyContents = true)
41 |
42 | let fcsWatcher, _ = fcsWatcherAndCompilerTmpAgent checker compilerTmpEmitter compiler coreConfig config.Fsproj
43 |
44 | if config.Watch then
45 | Console.ReadLine() |> ignore
46 | elif config.Eval then
47 | let makeSourceFileChanges fullPaths =
48 | let makeFileChange fullPath : FileChange =
49 | let fullPath = Path.getFullName fullPath
50 |
51 | { FullPath = fullPath
52 | Name = Path.GetFileName fullPath
53 | Status = FileStatus.Changed }
54 |
55 | FcsWatcherMsg.DetectSourceFileChanges (List.map makeFileChange fullPaths)
56 |
57 | let cache = fcsWatcher.PostAndReply FcsWatcherMsg.GetCache
58 |
59 | fcsWatcher.PostAndReply (makeSourceFileChanges [cache.EntryCrackedFsproj.SourceFiles.[0]])
60 |
61 | fcsWatcher.PostAndReply FcsWatcherMsg.WaitCompiled
62 | |> ignore
63 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Catalog.Journal/Masse.Catalog.Journal.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | netcoreapp2.1
5 | MasseCatalogJournal
6 | Lambda
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Always
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/FcsWatch.Binary/VsCodeHelper.fs:
--------------------------------------------------------------------------------
1 | namespace FcsWatch.Binary
2 | open Fake.IO
3 | open FcsWatch.Core
4 | open Fake.IO.FileSystemOperators
5 |
6 |
7 | module internal VscodeHelper =
8 |
9 | type Configuration =
10 | { name: string
11 | ``type``: string
12 | request: string
13 | preLaunchTask: obj
14 | program: obj
15 | args: obj
16 | processId: obj
17 | justMyCode: obj
18 | cwd: obj
19 | stopAtEntry: obj
20 | console: obj }
21 |
22 | type Launch =
23 | { version: string
24 | configurations: Configuration list }
25 |
26 | []
27 | module Launch =
28 | open Newtonsoft.Json
29 |
30 | let read file =
31 |
32 | let jsonTest = File.readAsString file
33 | JsonConvert.DeserializeObject jsonTest
34 |
35 | let write file (launch: Launch) =
36 | let settings = JsonSerializerSettings(NullValueHandling = NullValueHandling.Ignore)
37 | let jsonText = JsonConvert.SerializeObject(launch,Formatting.Indented,settings)
38 | File.writeString false file jsonText
39 |
40 | let writePid configurationName pid (launch: Launch) =
41 | { launch with
42 | configurations =
43 | let prediate configuration = configuration.request = "attach" && configuration.name = configurationName
44 |
45 | launch.configurations
46 | |> List.tryFind prediate
47 | |> function
48 | | Some _ -> ()
49 | | None ->
50 | logger.Info "Cannot find attachable configuration named %s in lanuch.json" configurationName
51 |
52 | let newConfigurations =
53 |
54 | launch.configurations
55 | |> List.map (fun configuration ->
56 | if prediate configuration then
57 | {configuration with processId = pid}
58 | else
59 | configuration
60 | )
61 |
62 | newConfigurations
63 | }
64 |
65 | []
66 | module File =
67 | let tryFindLaunchJsonUp workingDir =
68 | let makePath root = root > ".vscode" > "launch.json"
69 | File.tryFindUntilRoot makePath workingDir
70 |
71 | let tryFindRootUpByLaunchJson workingDir =
72 | tryFindLaunchJsonUp workingDir
73 | |> Option.map (fun path ->
74 | path
75 | |> Path.getDirectory
76 | |> Path.getDirectory
77 | )
--------------------------------------------------------------------------------
/src/FcsWatch.Core/Logger.fs:
--------------------------------------------------------------------------------
1 | namespace FcsWatch.Core
2 | open Fake.Core
3 |
4 |
5 | []
6 | module Logger =
7 | open System
8 |
9 | []
10 | type Level =
11 | | Minimal
12 | | Normal
13 | | Quiet
14 | | Debug
15 |
16 | type Logger internal (level) =
17 |
18 | let timeStamp (time:DateTime) = time.ToString("yyyy-MM-dd HH:mm:ss.fff")
19 |
20 | let withTimeStamp (f: string -> unit) =
21 | fun message ->
22 | let now = timeStamp DateTime.UtcNow
23 | sprintf "%s %s" now message
24 | |> f
25 |
26 |
27 | let _debug trace message =
28 | match level with
29 | | Level.Minimal -> ()
30 | | Level.Normal -> ()
31 | | Level.Quiet -> ()
32 | | Level.Debug -> trace message
33 |
34 |
35 | let _info trace message =
36 | match level with
37 | | Level.Quiet -> ()
38 | | Level.Minimal -> ()
39 | | Level.Normal -> trace message
40 | | Level.Debug -> trace message
41 |
42 | let _important trace message =
43 | match level with
44 | | Level.Quiet -> ()
45 | | _ -> trace message
46 |
47 | let _warn message = Trace.traceImportant message
48 |
49 | let _error message = Trace.traceError message
50 |
51 | member x.Info format =
52 | Printf.ksprintf (_info Trace.log) format
53 |
54 | member x.Debug format =
55 | Printf.ksprintf (_debug Trace.log) format
56 |
57 | member x.InfoGreen format =
58 | Printf.ksprintf (_info Trace.trace) format
59 |
60 | member x.Diagnostics text =
61 | System.Diagnostics.Debugger.Log(1,"",sprintf "%s %s\n" (timeStamp DateTime.UtcNow) text)
62 |
63 | /// with timeStamp
64 | member x.Infots format =
65 | Printf.ksprintf (withTimeStamp (_info Trace.log)) format
66 |
67 | member x.InfoGreents format =
68 | Printf.ksprintf (withTimeStamp (_info Trace.trace)) format
69 |
70 | /// LG = light gray
71 | member x.ImportantLG format =
72 | Printf.ksprintf (_important Trace.log) format
73 |
74 | member x.Important format = Printf.ksprintf (_important (printfn "%s")) format
75 |
76 | member x.ImportantGreen format =
77 | Printf.ksprintf (_important Trace.trace) format
78 |
79 | member x.Importantts format =
80 | Printf.ksprintf (withTimeStamp (_important Trace.log)) format
81 |
82 | member x.ImportantGreents format =
83 | Printf.ksprintf (withTimeStamp (_important Trace.trace)) format
84 |
85 | member x.Warn format =
86 | Printf.ksprintf _warn format
87 |
88 | member x.Error format =
89 | Printf.ksprintf _error format
90 |
91 | let create level = Logger(level)
92 |
--------------------------------------------------------------------------------
/RELEASE_NOTES.md:
--------------------------------------------------------------------------------
1 | # Release Notes
2 |
3 | ## 0.7.15-alpha - tbd
4 |
5 | ## 0.7.14 - 2019-07-26
6 | * using CTRL + C exit fcswatch-cli
7 |
8 | ## 0.7.13 - 2019-6-14
9 | * Support watching additional content files #40 @ @tylerhartwig
10 |
11 | ## 0.7.12 - 2019-6-14
12 | * Support watching additional content files #40 @ @tylerhartwig
13 |
14 | ## 0.7.11 - 2019-05-28
15 | * PORTA: Fixes missing RPrim_float32 #39 @7sharp9
16 |
17 | ## 0.7.10 - 2019-05-15
18 | * Add support for Bolero (detect "blazor serve" and copy dll to dist) #36
19 |
20 | ## 0.7.9 - 2019-04-29
21 | * Map project Other options --doc:path to --doc:fullPath #30 #31
22 | * Monitor error infos in MailBoxProcesser #16 #31
23 |
24 | ## 0.7.8 - 2019-04-27
25 | * Some as 0.7.6
26 |
27 | ## 0.7.7 - 2019-04-27
28 | * Some as 0.7.6
29 |
30 | ## 0.7.6 - 2019-04-27
31 | * Added `--configuration release` and `--framework netframework`
32 |
33 | ## 0.7.5 - 2019-04-18
34 | * Refix Send web hook after program (re)run #18
35 |
36 | ## 0.7.4 - 2019-04-18
37 | * UNIX: Fixed easyGetAllProjPaths #22
38 |
39 | ## 0.7.3 - 2019-04-17
40 | * Send web hook after program (re)run #18
41 |
42 | ## 0.7.2 - 2019-04-13
43 | * Refix Allows passing arguments to binary through fcswatch tool #14
44 |
45 | ## 0.7.1 - 2019-04-13
46 | * Allows passing arguments to binary through fcswatch tool #14
47 |
48 | ## 0.7.0 - 2019-04-12
49 | * Port out FcsWatch.Core used by FcsWatch.Binary and FcsWatch.Prota
50 | * Mark a lot unrelated types as internal and private
51 | * Shared file should trigger both projects compiling #11
52 | * Change Some Post to PostAndReply (more easy to test)
53 | * Try to add porta part to current solution
54 |
55 | ## 0.6.5 - 2019-03-24
56 | * Kill running project after watch mode is interrupt
57 |
58 | ## 0.6.4 - 2019-03-24
59 | * Pack as tool
60 |
61 | ## 0.6.3 - 2019-03-24
62 | * Using shared file between cli
63 |
64 | ## 0.6.2 - 2019-03-24
65 | * PrivateAssets FcsWatch-Helper.csproj
66 |
67 | ## 0.6.1 - 2019-03-24
68 | * Nuget package Include FcsWatch.Helpers
69 |
70 | ## 0.6.0 - 2019-03-24
71 | * Added autoReload mode
72 | * FcsWatch Cli Support
73 |
74 | ## 0.5.1 - 2019-03-18
75 | * BUGFIX: (Fetch full framework Cracked fsproj) List.exists -> List.forAll
76 | * Using FPublisher cli
77 |
78 | ## 0.5.0 - 2019-02-23
79 | * Multiple target frameworks
80 | * Refator to more readable codes
81 | * Deep copy dlls from obj to bin
82 | * Remove Unnessary dependecies
83 | * Using lightweight server --- Suave
84 | * FPublisher support
85 |
86 | ## 0.4.0 - 2019-01-23
87 | * Compile multiple files at some time(multiple projects)
88 |
89 | ## 0.3.0 - 2019-01-22
90 | * Added At once watch mode
91 | * Added Plugin mode
92 |
93 | ## 0.2.0 - 2019-01-17
94 | * Add fs file to project without interrupting watcher
95 |
96 | ## 0.1.2 - 2019-01-15
97 | * Refector to actor model
98 | * Warm compile
99 | * Add nuget icon to README
100 |
101 | ## 0.1.1-beta00023 - 2019-01-15
102 | * First Draft Version
103 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | node_modules
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.sln.docstates
10 |
11 | # Xamarin Studio / monodevelop user-specific
12 | *.userprefs
13 | *.dll.mdb
14 | *.exe.mdb
15 | tests/FsLive.Porta.Cli.Tests/data/**
16 |
17 | # Build results
18 |
19 | [Dd]ebug/
20 | [Rr]elease/
21 | x64/
22 | build/
23 | [Bb]in/
24 | [Oo]bj/
25 |
26 | # MSTest test Results
27 | [Tt]est[Rr]esult*/
28 | [Bb]uild[Ll]og.*
29 |
30 | *_i.c
31 | *_p.c
32 | *.ilk
33 | *.meta
34 | *.obj
35 | *.pch
36 | *.pdb
37 | *.pgc
38 | *.pgd
39 | *.rsp
40 | *.sbr
41 | *.tlb
42 | *.tli
43 | *.tlh
44 | *.tmp
45 | *.tmp_proj
46 | *.log
47 | *.vspscc
48 | *.vssscc
49 | .builds
50 | *.pidb
51 | *.log
52 | *.scc
53 |
54 | # Visual C++ cache files
55 | ipch/
56 | *.aps
57 | *.ncb
58 | *.opensdf
59 | *.sdf
60 | *.cachefile
61 |
62 | # Visual Studio profiler
63 | *.psess
64 | *.vsp
65 | *.vspx
66 |
67 | # Other Visual Studio data
68 | .vs/
69 |
70 | # Guidance Automation Toolkit
71 | *.gpState
72 |
73 | # ReSharper is a .NET coding add-in
74 | _ReSharper*/
75 | *.[Rr]e[Ss]harper
76 |
77 | # TeamCity is a build add-in
78 | _TeamCity*
79 |
80 | # DotCover is a Code Coverage Tool
81 | *.dotCover
82 |
83 | # NCrunch
84 | *.ncrunch*
85 | .*crunch*.local.xml
86 |
87 | # Installshield output folder
88 | [Ee]xpress/
89 |
90 | # DocProject is a documentation generator add-in
91 | DocProject/buildhelp/
92 | DocProject/Help/*.HxT
93 | DocProject/Help/*.HxC
94 | DocProject/Help/*.hhc
95 | DocProject/Help/*.hhk
96 | DocProject/Help/*.hhp
97 | DocProject/Help/Html2
98 | DocProject/Help/html
99 |
100 | # Click-Once directory
101 | publish/
102 |
103 | # Publish Web Output
104 | *.Publish.xml
105 |
106 | # Enable nuget.exe in the .nuget folder (though normally executables are not tracked)
107 | !.nuget/NuGet.exe
108 |
109 | # Windows Azure Build Output
110 | csx
111 | *.build.csdef
112 |
113 | # Windows Store app package directory
114 | AppPackages/
115 |
116 |
117 | # Others
118 | sql/
119 | *.Cache
120 | ClientBin/
121 | [Ss]tyle[Cc]op.*
122 | ~$*
123 | *~
124 | *.dbmdl
125 | *.[Pp]ublish.xml
126 | *.pfx
127 | *.publishsettings
128 |
129 | # RIA/Silverlight projects
130 | Generated_Code/
131 |
132 | # Backup & report files from converting an old project file to a newer
133 | # Visual Studio version. Backup files are not needed, because we have git ;-)
134 | _UpgradeReport_Files/
135 | Backup*/
136 | UpgradeLog*.XML
137 | UpgradeLog*.htm
138 |
139 | # SQL Server files
140 | App_Data/*.mdf
141 | App_Data/*.ldf
142 |
143 |
144 | #LightSwitch generated files
145 | GeneratedArtifacts/
146 | _Pvt_Extensions/
147 | ModelManifest.xml
148 |
149 | # =========================
150 | # Windows detritus
151 | # =========================
152 |
153 | # Windows image file caches
154 | Thumbs.db
155 | ehthumbs.db
156 |
157 | # Folder config file
158 | Desktop.ini
159 |
160 | # Recycle Bin used on file shares
161 | $RECYCLE.BIN/
162 |
163 | # Mac desktop service store files
164 | .DS_Store
165 |
166 | # ===================================================
167 | # Exclude F# project specific directories and files
168 | # ===================================================
169 |
170 | # NuGet Packages Directory
171 | packages/
172 |
173 | # Test results produced by build
174 | TestResults.xml
175 |
176 | # Nuget outputs
177 | nuget/*.nupkg
178 | release.cmd
179 | release.sh
180 | localpackages/
181 | paket-files
182 | *.orig
183 | docsrc/content/license.md
184 | docsrc/content/release-notes.md
185 | .fake
186 | docsrc/tools/FSharp.Formatting.svclog
187 |
188 | .ionide/
--------------------------------------------------------------------------------
/src/fcswatch-cli/Share.fs:
--------------------------------------------------------------------------------
1 | module FcsWatch.Cli.Share
2 |
3 | open System
4 | open FcsWatch.Core
5 | open FcsWatch.Binary
6 | open Argu
7 | open Fake.IO.Globbing.Operators
8 | open Fake.IO.FileSystemOperators
9 | open Fake.Core
10 | open FcsWatch.Core.Types
11 |
12 | let private defaultUrl = "http://localhost:9867/update"
13 |
14 | type Arguments =
15 | | Working_Dir of string
16 | | Project_File of string
17 | | Debuggable
18 | | Logger_Level of Logger.Level
19 | | No_Build
20 | | Webhook of string
21 | | Send
22 | | [] Framework of string
23 | | [] Configuration of Configuration
24 | with
25 | interface IArgParserTemplate with
26 | member x.Usage =
27 | match x with
28 | | Working_Dir _ -> "Specfic working directory, default is current directory"
29 | | Project_File _ -> "Entry project file, default is exact fsproj file in working dir"
30 | | Debuggable _ -> "Enable debuggable in vscode, This will disable auto Reload"
31 | | Logger_Level _ -> "Default is Minimal"
32 | | No_Build -> "--no-build"
33 | | Webhook _ -> "send a web hook when program (re)run"
34 | | Send _ -> sprintf "Equivalent to --webhook %s" defaultUrl
35 | | Framework _ -> "The target framework to build for. The default to prefer netcore."
36 | | Configuration _ -> "(experienment)The configuration to use for building the project. The default is Debug."
37 |
38 | type ProcessResult =
39 | { Config: BinaryConfig
40 | ProjectFile: string }
41 |
42 | let processParseResults additionalBinaryArgs (results: ParseResults) =
43 | let execContext = Fake.Core.Context.FakeExecutionContext.Create false "generate.fsx" []
44 | Fake.Core.Context.setExecutionContext (Fake.Core.Context.RuntimeContext.Fake execContext)
45 | let defaultConfigValue = BinaryConfig.DefaultValue
46 |
47 | let workingDir = results.GetResult (Working_Dir,defaultConfigValue.WorkingDir)
48 |
49 | let projectFile =
50 | match results.TryGetResult Project_File with
51 | | Some projectFile -> projectFile
52 | | None ->
53 | (!! (workingDir > "*.fsproj")
54 | |> Seq.filter (fun file -> file.EndsWith ".fsproj")
55 | |> List.ofSeq
56 | |> function
57 | | [ ] ->
58 | failwithf "no project file found, no compilation arguments given and no project file found in \"%s\"" Environment.CurrentDirectory
59 | | [ file ] ->
60 | printfn "using implicit project file '%s'" file
61 | file
62 | | file1 :: file2 :: _ ->
63 | failwithf "multiple project files found, e.g. %s and %s" file1 file2 )
64 |
65 | let noBuild =
66 | match results.TryGetResult No_Build with
67 | | Some _ -> true
68 | | None ->
69 | false
70 |
71 | let webhook =
72 | match results.TryGetResult Send, results.TryGetResult Webhook with
73 | | Some _, _ -> Some defaultUrl
74 | | _, Some webhook -> Some webhook
75 | | _ -> None
76 |
77 | { ProjectFile = projectFile
78 | Config =
79 | { BinaryConfig.DefaultValue with
80 | WorkingDir = workingDir
81 | DevelopmentTarget =
82 | match results.TryGetResult Debuggable with
83 | | Some _ -> DevelopmentTarget.debuggableProgram
84 | | None -> DevelopmentTarget.autoReloadProgram
85 |
86 | LoggerLevel = results.GetResult(Logger_Level, defaultConfigValue.LoggerLevel)
87 | NoBuild = noBuild
88 | Framework = results.TryGetResult Framework
89 | Configuration = results.GetResult(Configuration, BinaryConfig.DefaultValue.Configuration)
90 | Webhook = webhook
91 | AdditionalSwitchArgs = additionalBinaryArgs }
92 | }
93 |
94 |
95 |
96 |
97 | let parser = ArgumentParser.Create(programName = "fcswatch.exe")
98 |
--------------------------------------------------------------------------------
/.paket/paket.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | true
7 | $(MSBuildThisFileDirectory)
8 | $(MSBuildThisFileDirectory)..\
9 | $(PaketRootPath)paket.lock
10 | $(PaketRootPath)paket-files\paket.restore.cached
11 | /Library/Frameworks/Mono.framework/Commands/mono
12 | mono
13 |
14 |
15 |
16 |
17 | $(PaketRootPath)paket.exe
18 | $(PaketToolsPath)paket.exe
19 | "$(PaketExePath)"
20 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)"
21 |
22 |
23 |
24 |
25 |
26 | $(MSBuildProjectFullPath).paket.references
27 |
28 |
29 |
30 |
31 | $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references
32 |
33 |
34 |
35 |
36 | $(MSBuildProjectDirectory)\paket.references
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | $(PaketCommand) restore --references-file "$(PaketReferences)"
49 |
50 | RestorePackages; $(BuildDependsOn);
51 |
52 |
53 |
54 | true
55 |
56 |
57 |
58 | $([System.IO.File]::ReadAllText('$(PaketRestoreCacheFile)'))
59 | $([System.IO.File]::ReadAllText('$(PaketLockFilePath)'))
60 | true
61 | false
62 | true
63 |
64 |
65 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/src/FcsWatch.Porta/CompilerTmpEmitter.fs:
--------------------------------------------------------------------------------
1 | namespace FcsWatch.Porta
2 | open Fake.Core
3 | open FcsWatch.Core
4 | open Types
5 | open FcsWatch.Core.CrackedFsproj
6 | open FcsWatch.Core.CompilerTmpEmitter
7 | open FSharp.Compiler.SourceCodeServices
8 | open FcsWatch.Porta.FromCompilerService
9 | open Extensions
10 |
11 | type PortaConfig =
12 | { Eval: bool
13 | LiveCheckOnly: bool
14 | LoggerLevel: Logger.Level
15 | Fsproj: string option
16 | UseEditFiles: bool
17 | WriteInfo: bool
18 | NoBuild: bool
19 | WorkingDir: string
20 | Webhook: string option
21 | Watch: bool
22 | OtherFlags: string [] }
23 |
24 |
25 |
26 |
27 |
28 | []
29 | module CompilerTmpEmitter =
30 | type TmpEmitterState = CompilerTmpEmitterState
31 |
32 | let mutable private count = 0
33 |
34 | []
35 | module CompilerTmpEmitterState =
36 |
37 | let private processResult config projOptions (result: CheckResult) =
38 | if result.ExitCode <> 0 then ()
39 | else
40 | match config.Webhook with
41 | | Some hook ->
42 | sendToWebHook config.Eval hook result.Contents
43 | | None ->
44 |
45 | if config.Eval then
46 | printfn "fscd: CHANGE DETECTED, RE-EVALUATING ALL INPUTS...."
47 | evaluateDecls config.Eval config.WriteInfo config.LiveCheckOnly result.Contents projOptions
48 |
49 | // The default is to dump
50 | if not config.Eval && config.Webhook.IsNone then
51 | let fileConvContents = jsonFiles config.Eval (Array.ofList result.Contents)
52 | count <- count + 1
53 | logger.Info "%A\nCount:%d" fileConvContents count
54 |
55 |
56 | let tryEmit (config: PortaConfig) (state: TmpEmitterState) =
57 | let commonState = state.CommonState
58 |
59 | let cache = commonState.CrackerFsprojFileBundleCache
60 |
61 | match commonState.CompilingNumber with
62 | | 0 ->
63 |
64 | logger.Info "Current cached compier task is %d" commonState.CachedCompilerTasks.Length
65 |
66 | match commonState.CachedCompilerTasks with
67 | | _ ->
68 | let lastTasks =
69 | commonState.CachedCompilerTasks
70 | |> List.groupBy (fun compilerTask ->
71 | compilerTask.Task.Result.[0].Fsproj.ProjPath
72 | )
73 | |> List.map (fun (projPath, compilerTasks) ->
74 | compilerTasks |> List.maxBy (fun compilerTask -> compilerTask.StartTime)
75 | )
76 |
77 |
78 | let allResults: CheckResult list = lastTasks |> List.collect (fun task -> task.Task.Result)
79 |
80 | match List.tryFind (fun (result: CheckResult) -> result.ExitCode <> 0) allResults with
81 | | Some result ->
82 | let errorText =
83 | result.Errors
84 | |> Seq.map (fun error -> error.ToString())
85 | |> String.concat "\n"
86 |
87 | state
88 |
89 | | None ->
90 |
91 | let projRefersMap = cache.ProjRefersMap
92 |
93 | let projLevelMap = cache.ProjLevelMap
94 |
95 | commonState.CompilerTmp
96 | |> Seq.sortByDescending (fun projPath ->
97 | projLevelMap.[projPath]
98 | )
99 | |> Seq.iter (fun projPath ->
100 | let correspondingResult =
101 | allResults |> List.find (fun result -> result.Fsproj.ProjPath = projPath)
102 |
103 | processResult config correspondingResult.Fsproj.FSharpProjectOptions correspondingResult
104 | )
105 | CompilerTmpEmitterState.createEmpty 0 cache
106 | | _ -> state
107 |
108 |
109 | let create config =
110 | { new ICompilerTmpEmitter with
111 | member x.TryEmit(workingDir, state) = CompilerTmpEmitterState.tryEmit config state
112 | member x.ProcessCustomMsg (_, state) = state
113 | member x.CustomInitialState = 0 }
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/src/FcsWatch.Porta.Interpreter/CodeModel.fs:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Fabulous contributors. See LICENSE.md for license.
2 |
3 | module FcsWatch.Porta.CodeModel
4 |
5 | type DRange =
6 | { File: string
7 | StartLine: int
8 | StartColumn: int
9 | EndLine: int
10 | EndColumn: int }
11 |
12 | /// A representation of resolved F# expressions that can be serialized
13 | type DExpr =
14 | | Value of DLocalRef
15 | | ThisValue of DType
16 | | BaseValue of DType
17 | | Application of DExpr * DType[] * DExpr[] * DRange option
18 | | Lambda of DType * DType * DLocalDef * DExpr
19 | | TypeLambda of DGenericParameterDef[] * DExpr
20 | | Quote of DExpr
21 | | IfThenElse of DExpr * DExpr * DExpr
22 | | DecisionTree of DExpr * (DLocalDef[] * DExpr)[]
23 | | DecisionTreeSuccess of int * DExpr[]
24 | | Call of DExpr option * DMemberRef * DType[] * DType[] * DExpr[] * DRange option
25 | | NewObject of DMemberRef * DType[] * DExpr[]
26 | | LetRec of ( DLocalDef * DExpr)[] * DExpr
27 | | Let of (DLocalDef * DExpr) * DExpr
28 | | NewRecord of DType * DExpr[]
29 | | ObjectExpr of DType * DExpr * DObjectExprOverrideDef[] * (DType * DObjectExprOverrideDef[])[]
30 | | FSharpFieldGet of DExpr option * DType * DFieldRef
31 | | FSharpFieldSet of DExpr option * DType * DFieldRef * DExpr
32 | | NewUnionCase of DType * DUnionCaseRef * DExpr[]
33 | | UnionCaseGet of DExpr * DType * DUnionCaseRef * DFieldRef
34 | | UnionCaseSet of DExpr * DType * DUnionCaseRef * DFieldRef * DExpr
35 | | UnionCaseTag of DExpr * DType
36 | | UnionCaseTest of DExpr * DType * DUnionCaseRef
37 | | TraitCall of DType[] * string * isInstance: bool * DType[] * DType[] * DExpr[] * DRange option
38 | | NewTuple of DType * DExpr[]
39 | | TupleGet of DType * int * DExpr
40 | | Coerce of DType * DExpr
41 | | NewArray of DType * DExpr[]
42 | | TypeTest of DType * DExpr
43 | | AddressSet of DExpr * DExpr
44 | | ValueSet of Choice * DExpr
45 | | Unused
46 | | DefaultValue of DType
47 | | Const of obj * DType
48 | | AddressOf of DExpr
49 | | Sequential of DExpr * DExpr
50 | | FastIntegerForLoop of DExpr * DExpr * DExpr * bool
51 | | WhileLoop of DExpr * DExpr
52 | | TryFinally of DExpr * DExpr
53 | | TryWith of DExpr * DLocalDef * DExpr * DLocalDef * DExpr
54 | | NewDelegate of DType * DExpr
55 | | ILFieldGet of DExpr option * DType * string
56 | | ILFieldSet of DExpr option * DType * string * DExpr
57 | | ILAsm of string * DType[] * DExpr[]
58 |
59 | and DType =
60 | | DNamedType of DEntityRef * DType[]
61 | | DFunctionType of DType * DType
62 | | DTupleType of bool * DType[]
63 | | DArrayType of int * DType
64 | | DByRefType of DType
65 | | DVariableType of string
66 |
67 | and DLocalDef =
68 | { Name: string
69 | IsMutable: bool
70 | Type: DType
71 | Range: DRange option
72 | IsCompilerGenerated: bool }
73 |
74 | and DMemberDef =
75 | { EnclosingEntity: DEntityRef
76 | Name: string
77 | GenericParameters: DGenericParameterDef[]
78 | IsInstance: bool
79 | IsValue: bool
80 | IsCompilerGenerated: bool
81 | Parameters: DLocalDef[]
82 | ReturnType: DType
83 | Range: DRange option }
84 |
85 | member x.Ref =
86 | { Entity=x.EnclosingEntity
87 | Name= x.Name
88 | GenericArity = x.GenericParameters.Length
89 | ArgTypes = (x.Parameters |> Array.map (fun p -> p.Type))
90 | ReturnType = x.ReturnType
91 | Range = x.Range }
92 |
93 | and DGenericParameterDef =
94 | { Name: string }
95 |
96 | and DEntityDef =
97 | { Name: string
98 | GenericParameters: DGenericParameterDef[]
99 | UnionCases: string[]
100 | Range: DRange option }
101 |
102 | and DEntityRef = DEntityRef of string
103 |
104 | and DMemberRef =
105 | { Entity: DEntityRef
106 | Name: string
107 | GenericArity: int
108 | ArgTypes: DType[]
109 | ReturnType: DType
110 | Range: DRange option }
111 |
112 | and DLocalRef =
113 | { Name: string
114 | IsThisValue: bool
115 | IsMutable: bool
116 | Range: DRange option }
117 |
118 | and DFieldRef = DFieldRef of int * string
119 |
120 | and DUnionCaseRef = DUnionCaseRef of string
121 |
122 | and DObjectExprOverrideDef =
123 | { Name: string
124 | GenericParameters: DGenericParameterDef[]
125 | Parameters: DLocalDef[]
126 | Body: DExpr }
127 |
128 | type DDecl =
129 | | DDeclEntity of DEntityDef * DDecl[]
130 | | DDeclMember of DMemberDef * DExpr * isLiveCheck: bool
131 | | InitAction of DExpr * DRange option
132 |
133 | type DFile =
134 | { Code: DDecl[] }
135 |
136 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.API/Masse.API.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 | MasseAPI
7 | true
8 | Lambda
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | PreserveNewest
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/src/FcsWatch.Binary/Extensions.fs:
--------------------------------------------------------------------------------
1 | namespace FcsWatch.Binary
2 | open System.IO
3 | open Fake.IO
4 | open Fake.IO.FileSystemOperators
5 | open FcsWatch.Core.CrackedFsproj
6 | open FSharp.Compiler.SourceCodeServices
7 | open FcsWatch.Core
8 | open Fake.DotNet
9 | open Fake.Core
10 | open FcsWatch.Core.Types
11 |
12 | type CompilerResult =
13 | { Dll: string
14 | Errors: FSharpErrorInfo []
15 | ExitCode: int
16 | ProjPath: string }
17 | with
18 | member x.Pdb = Path.changeExtension ".pdb" x.Dll
19 |
20 | interface ICompilerOrCheckResult with
21 | member x.Errors = x.Errors
22 | member x.ExitCode = x.ExitCode
23 | member x.ProjPath = x.ProjPath
24 |
25 | []
26 | module internal Global =
27 | let mutable logger = Logger.create Logger.Level.Minimal
28 |
29 | let private dotnetWith command args dir =
30 | DotNet.exec
31 | (fun ops -> {ops with WorkingDirectory = dir})
32 | command
33 | (Args.toWindowsCommandLine args)
34 |
35 | let dotnet command args dir =
36 | let result = dotnetWith command args dir
37 | if result.ExitCode <> 0
38 | then failwithf "Error while running %s with args %A" command (List.ofSeq args)
39 |
40 |
41 | module Extensions =
42 |
43 |
44 | type internal Logger.Logger with
45 | member x.CopyFile src dest =
46 | File.Copy(src,dest,true)
47 | logger.Important "%s ->\n%s" src dest
48 |
49 | /// In release configuration, still copy pdb
50 | member x.CopyPdb _configuration src dest =
51 | x.CopyFile src dest
52 |
53 | []
54 | module SingleTargetCrackedFsproj =
55 |
56 |
57 | let copyFileFromRefDllToBin (configuration: Configuration) originProjectFile (destCrackedFsprojSingleTarget: SingleTargetCrackedFsproj) =
58 |
59 | let targetDir = destCrackedFsprojSingleTarget.TargetDir
60 |
61 | let originDll =
62 | let projName = Path.GetFileNameWithoutExtension originProjectFile
63 |
64 | destCrackedFsprojSingleTarget.RefDlls
65 | |> Array.find(fun refDll -> Path.GetFileNameWithoutExtension refDll = projName)
66 |
67 | let fileName = Path.GetFileName originDll
68 |
69 | let destDll = targetDir > fileName
70 |
71 | logger.CopyFile originDll destDll
72 |
73 | logger.CopyPdb configuration (Path.changeExtension ".pdb" originDll) (Path.changeExtension ".pdb" destDll)
74 |
75 | destCrackedFsprojSingleTarget.AdditionalTargetDirs |> Seq.iter (fun targetDir ->
76 | let destDll = targetDir > fileName
77 | logger.CopyFile originDll destDll
78 | logger.CopyPdb configuration (Path.changeExtension ".pdb" originDll) (Path.changeExtension ".pdb" destDll))
79 |
80 |
81 | let copyObjToBin configuration (singleTargetCrackedFsproj: SingleTargetCrackedFsproj) =
82 | logger.CopyFile singleTargetCrackedFsproj.ObjTargetFile singleTargetCrackedFsproj.TargetPath
83 |
84 | logger.CopyPdb configuration singleTargetCrackedFsproj.ObjTargetPdb singleTargetCrackedFsproj.TargetPdbPath
85 |
86 | singleTargetCrackedFsproj.AdditionalTargetDirs |> Seq.iter (fun targetDir ->
87 | logger.CopyFile singleTargetCrackedFsproj.ObjTargetFile targetDir
88 | logger.CopyPdb configuration singleTargetCrackedFsproj.ObjTargetPdb targetDir)
89 |
90 |
91 | let compile (checker: FSharpChecker) (crackedProjectSingleTarget: SingleTargetCrackedFsproj) = async {
92 | let tmpDll = crackedProjectSingleTarget.ObjTargetFile
93 |
94 | let baseOptions =
95 | crackedProjectSingleTarget.FSharpProjectOptions.OtherOptions
96 | |> Array.map (fun op -> if op.StartsWith "-o:" then "-o:" + tmpDll else op)
97 |
98 | let fscArgs = Array.concat [[|"fsc.exe"|]; baseOptions;[|"--nowin32manifest"|]]
99 | let! errors,exitCode = checker.Compile(fscArgs)
100 | return
101 | { Errors = errors
102 | ExitCode = exitCode
103 | Dll = tmpDll
104 | ProjPath = crackedProjectSingleTarget.ProjPath }
105 | }
106 |
107 | []
108 | module CrackedFsproj =
109 |
110 | let copyFileFromRefDllToBin configuration projectFile (destCrackedFsproj: CrackedFsproj) =
111 | destCrackedFsproj.AsList
112 | |> List.iter (SingleTargetCrackedFsproj.copyFileFromRefDllToBin configuration projectFile)
113 |
114 | let copyObjToBin configuration (crackedFsproj: CrackedFsproj) =
115 | crackedFsproj.AsList |> List.iter (SingleTargetCrackedFsproj.copyObjToBin configuration)
116 |
117 |
118 | let compile (checker: FSharpChecker) (crackedFsProj: CrackedFsproj) =
119 | crackedFsProj.AsList
120 | |> List.map (SingleTargetCrackedFsproj.compile checker)
121 | |> Async.Parallel
122 |
123 | []
124 | module internal InternalExtensions =
125 | []
126 | module File =
127 | let rec tryFindUntilRoot makePath dir =
128 | let file = makePath dir
129 | match file with
130 | | null -> None
131 | | _ ->
132 | if File.exists file
133 | then Some file
134 | else tryFindUntilRoot makePath (Path.getDirectory dir)
--------------------------------------------------------------------------------
/src/FcsWatch.Porta/Extensions.fs:
--------------------------------------------------------------------------------
1 | namespace FcsWatch.Porta
2 |
3 | open System.IO
4 | open Fake.IO
5 | open FcsWatch.Core.CrackedFsproj
6 | open FSharp.Compiler.SourceCodeServices
7 | open FcsWatch.Core
8 | open FcsWatch.Core.Types
9 |
10 | type CheckResult =
11 | { ExitCode: int
12 | Errors: FSharpErrorInfo []
13 | Fsproj: SingleTargetCrackedFsproj
14 | Contents: FSharpImplementationFileContents list }
15 | with
16 | interface ICompilerOrCheckResult with
17 | member x.ExitCode = x.ExitCode
18 | member x.Errors = x.Errors
19 | member x.ProjPath = x.Fsproj.ProjPath
20 |
21 | []
22 | module internal Global =
23 | let mutable logger = Logger.create Logger.Level.Minimal
24 |
25 | module Extensions =
26 |
27 | []
28 | module SingleTargetCrackedFsproj =
29 |
30 | let private mapProjOptionsWithoutSourceFiles (projOptions: FSharpProjectOptions) =
31 | { projOptions with
32 | OtherOptions =
33 | projOptions.OtherOptions
34 | |> Array.filter (fun (op: string) -> not (op.EndsWith ".fs" || op.EndsWith ".fsi" || op.EndsWith ".fsx"))
35 | }
36 | let check useEditFiles liveCheckOnly (checker: FSharpChecker) (singleTargetCrackedFsproj: SingleTargetCrackedFsproj) = async {
37 | let rec checkFile1 count sourceFile =
38 | try
39 | let _, checkResults = checker.ParseAndCheckFileInProject(sourceFile, 0, FileSystem.readFile sourceFile useEditFiles, mapProjOptionsWithoutSourceFiles singleTargetCrackedFsproj.FSharpProjectOptions) |> Async.RunSynchronously
40 | match checkResults with
41 | | FSharpCheckFileAnswer.Aborted ->
42 | logger.Important "aborted"
43 | Result.Error (None,[||])
44 |
45 | | FSharpCheckFileAnswer.Succeeded res ->
46 | let mutable hasErrors = false
47 | for error in res.Errors do
48 | if error.Severity = FSharpErrorSeverity.Error then
49 | hasErrors <- true
50 |
51 | if hasErrors then
52 | Result.Error (res.ImplementationFile, res.Errors)
53 | else
54 | Result.Ok res.ImplementationFile
55 | with
56 | | :? System.IO.IOException when count = 0 ->
57 | System.Threading.Thread.Sleep 500
58 | checkFile1 1 sourceFile
59 | | exn ->
60 | logger.Error "%s" (exn.ToString())
61 | Result.Error (None,[||])
62 |
63 | let checkFile2 file =
64 | match checkFile1 0 (Path.GetFullPath(file)) with
65 | // Note, if livechecks are on, we continue on regardless of errors
66 | | Result.Error (iopt, errors) when not liveCheckOnly ->
67 | logger.Important "fscd: ERRORS for %s" file
68 | Result.Error errors
69 | | Result.Error (iopt, errors) ->
70 | match iopt with
71 | | None -> Result.Error errors
72 | | Some i ->
73 | logger.Important "fscd: GOT PortaCode for %s" file
74 | Result.Ok (i,errors)
75 | | Result.Ok iopt ->
76 | match iopt with
77 | | None -> Result.Error [||]
78 | | Some i ->
79 | logger.Important "fscd: GOT PortaCode for %s" file
80 | Result.Ok (i, [||])
81 |
82 | let sourceFiles = singleTargetCrackedFsproj.SourceFiles
83 | let allResults =
84 | sourceFiles
85 | |> Array.map checkFile2
86 |
87 | let isError result =
88 | match result with
89 | | Result.Error _ -> true
90 | | Result.Ok _ -> false
91 |
92 | return
93 | if Array.exists isError allResults
94 | then
95 | { ExitCode = -1
96 | Errors =
97 | allResults |> Array.choose (fun result ->
98 | match result with
99 | | Result.Error errors -> Some (errors)
100 | | Result.Ok (_,errors) -> Some (errors)
101 | )
102 | |> Array.concat
103 | Fsproj = singleTargetCrackedFsproj
104 | Contents = [] }
105 | else
106 | let contents, errors =
107 | allResults
108 | |> Array.choose (fun result ->
109 | match result with
110 | | Result.Error _ -> None
111 | | Result.Ok (contents, errors) -> Some (contents, errors)
112 | )
113 | |> Array.unzip
114 |
115 | { ExitCode = 0
116 | Contents = List.ofArray contents
117 | Errors = Array.concat errors
118 | Fsproj = singleTargetCrackedFsproj }
119 | }
120 |
121 |
122 | []
123 | module CrackedFsproj =
124 |
125 | let check useEditFiles liveCheckOnly (checker: FSharpChecker) (crackedFsproj: CrackedFsproj) =
126 | SingleTargetCrackedFsproj.check useEditFiles liveCheckOnly checker crackedFsproj.PreferSingleTargetCrackedFsproj
127 |
128 |
--------------------------------------------------------------------------------
/src/fslive-cli/fslive.fs:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Fabulous contributors. See LICENSE.md for license.
2 |
3 | // F# Compiler Daemon sample
4 | //
5 | // Sample use, assumes app has a reference to ELmish.XamrinForms.LiveUpdate:
6 | //
7 | // cd Fabulous\Samples\CounterApp\CounterApp
8 | // adb -d forward tcp:9867 tcp:9867
9 | // dotnet run --project ..\..\..\Fabulous.Cli\Fabulous.Cli.fsproj -- --eval @out.args
10 | // dotnet run --project ..\..\..\Fabulous.Cli\Fabulous.Cli.fsproj -- --watch --webhook:http://localhost:9867/update @out.args
11 |
12 | module FsLive.Driver
13 |
14 | open FcsWatch.Porta
15 | open FcsWatch.Core
16 | open System.IO
17 | open System
18 |
19 |
20 |
21 | #if !TEST
22 | []
23 | #endif
24 | let main (argv: string[]) =
25 |
26 | let mutable fsproj = None
27 | let mutable eval = false
28 | let mutable livechecksonly = false
29 | let mutable watch = false
30 | let mutable useEditFiles = false
31 | let mutable loggerLevel = Logger.Level.Minimal
32 | let mutable writeinfo = false
33 | let mutable webhook = None
34 | let mutable otherFlags = []
35 | let defaultUrl = "http://localhost:9867/update"
36 | let fsharpArgs =
37 | let mutable haveDashes = false
38 |
39 | [| for arg in argv do
40 | let arg = arg.Trim()
41 | if arg.StartsWith("@") then
42 | for line in File.ReadAllLines(arg.[1..]) do
43 | let line = line.Trim()
44 | if not (String.IsNullOrWhiteSpace(line)) then
45 | yield line
46 | elif arg.EndsWith(".fsproj") then
47 | fsproj <- Some arg
48 | elif arg = "--" then haveDashes <- true
49 | elif arg.StartsWith "--define:" then otherFlags <- otherFlags @ [ arg ]
50 | elif arg = "--watch" then watch <- true
51 | elif arg = "--eval" then eval <- true
52 | elif arg = "--livechecksonly" then livechecksonly <- true
53 | elif arg = "--writeinfo" then writeinfo <- true
54 | elif arg = "--vshack" then useEditFiles <- true
55 | elif arg.StartsWith "--webhook:" then webhook <- Some arg.["--webhook:".Length ..]
56 | elif arg.StartsWith "--loggerlevel:" then
57 | loggerLevel <-
58 | match arg.["--loggerlevel:".Length ..] with
59 | | "0" -> Logger.Level.Quiet
60 | | "1" -> Logger.Level.Minimal
61 | | "2" -> Logger.Level.Normal
62 | | s -> failwithf "invalid logger level number %s" s
63 |
64 | elif arg = "--send" then webhook <- Some defaultUrl
65 | elif arg = "--version" then
66 | printfn ""
67 | printfn "*** NOTE: if sending the code to a device the versions of CodeModel.fs and Interpreter.fs on the device must match ***"
68 | printfn ""
69 | printfn "CLI tool assembly version: %A" (System.Reflection.Assembly.GetExecutingAssembly().GetName().Version)
70 | printfn "CLI tool name: %s" (System.Reflection.Assembly.GetExecutingAssembly().GetName().Name)
71 | printfn ""
72 | elif arg = "--help" then
73 | printfn "Command line tool for watching and interpreting F# projects"
74 | printfn ""
75 | printfn "Usage: arg .. arg [-- ]"
76 | printfn " @args.rsp [-- ]"
77 | printfn " ... Project.fsproj ... [-- ]"
78 | printfn ""
79 | printfn "The default source is a single project file in the current directory."
80 | printfn "The default output is a JSON dump of the PortaCode."
81 | printfn ""
82 | printfn "Arguments:"
83 | printfn " --watch Watch the source files of the project for changes"
84 | printfn " --webhook: Send the JSON-encoded contents of the PortaCode to the webhook"
85 | printfn " --send Equivalent to --webhook:%s" defaultUrl
86 | printfn " --eval Evaluate the contents using the interpreter after each update"
87 | printfn " --livechecksonly (Experimental) Only evaluate declarations with a LiveCheck attribute"
88 | printfn " This uses on-demand execution semantics for top-level declarations"
89 | printfn " --loggerlevel:<0|1|2> 0: Quiet; 1: Minimal; 2: Normal; Default is Minimal"
90 | printfn " --writeinfo (Experimental) Write an info file based on results of evaluation"
91 | printfn " --vshack (Experimental) Watch for .fsharp/foo.fsx.edit files and use the contents of those"
92 | printfn " All other args are assumed to be extra F# command line arguments"
93 | exit 1
94 | else yield arg |]
95 |
96 |
97 | let config: PortaConfig =
98 | { Eval = eval
99 | LiveCheckOnly = livechecksonly
100 | LoggerLevel = loggerLevel
101 | Fsproj = fsproj
102 | UseEditFiles = useEditFiles
103 | WriteInfo = writeinfo
104 | NoBuild = true
105 | WorkingDir = Directory.GetCurrentDirectory()
106 | Webhook = webhook
107 | Watch = watch
108 | OtherFlags = Array.append fsharpArgs (Array.ofList otherFlags) }
109 |
110 | try
111 | System.Environment.SetEnvironmentVariable("LIVECHECK", "1")
112 | runFcsWatcher config
113 | 0
114 | with e ->
115 | printfn "Error: %s" (e.ToString())
116 | 1
117 |
--------------------------------------------------------------------------------
/src/FcsWatch.Core/Compiler.fs:
--------------------------------------------------------------------------------
1 | module FcsWatch.Core.Compiler
2 | open Types
3 | open System.Diagnostics
4 | open FcsWatch.Core.CompilerTmpEmitter
5 | open System
6 | open FcsWatch.Core.CrackedFsproj
7 | open FSharp.Compiler.SourceCodeServices
8 |
9 |
10 |
11 | []
12 | type CompilerMsg =
13 | | UpdateCache of CrackedFsprojBundleCache
14 | | CompileProjects of why: WhyCompile * projects: CrackedFsproj list * incrCompilingNumberChannel: AsyncReplyChannel
15 |
16 | type CompilerModel =
17 | { CrackerFsprojBundleCache: CrackedFsprojBundleCache }
18 |
19 |
20 | type ICompiler<'Result when 'Result :> ICompilerOrCheckResult> =
21 | abstract member Compile : checker: FSharpChecker * proejct: CrackedFsproj -> Async<'Result []>
22 | abstract member WarmCompile: bool
23 | abstract member Summary: result: 'Result * elapsed: int64 -> string
24 |
25 | let compilerAgent (compiler: ICompiler<'Result>) (compilerTmpEmitterAgent: MailboxProcessor>) (initialCache: CrackedFsprojBundleCache) checker = MailboxProcessor.Start(fun inbox ->
26 | inbox.Error.Add(fun error -> logger.Error "%A" error)
27 |
28 | let createCompileTask (crackedFsprojs: CrackedFsproj list) =
29 | crackedFsprojs
30 | |> List.map (fun crackedFsproj ->
31 | async {
32 | let stopWatch = Stopwatch.StartNew()
33 |
34 | let! compilerResults = compiler.Compile (checker, crackedFsproj)
35 |
36 | match Array.tryFind (fun (compilerResult: 'Result) -> compilerResult.ExitCode <> 0) compilerResults with
37 | | None ->
38 | let compilerResult = compilerResults.[0]
39 |
40 | ICompilerOrCheckResult.processCompileOrCheckResult compilerResult
41 |
42 | logger.Important "%s" (compiler.Summary (compilerResult, stopWatch.ElapsedMilliseconds))
43 |
44 | compilerTmpEmitterAgent.Post (CompilerTmpEmitterMsg.addTmp crackedFsproj.ProjPath)
45 |
46 | return compilerResult
47 |
48 | | Some erroCompilerResult ->
49 | ICompilerOrCheckResult.processCompileOrCheckResult erroCompilerResult
50 |
51 | return erroCompilerResult
52 | }
53 | )
54 | |> Async.Parallel
55 |
56 |
57 | let rec loop model = async {
58 | let projectMap = model.CrackerFsprojBundleCache.ProjectMap
59 |
60 | let! msg = inbox.Receive()
61 | match msg with
62 | | CompilerMsg.CompileProjects (why, crackedFsprojs, replyChannel) ->
63 | compilerTmpEmitterAgent.PostAndReply (fun replyChannel -> CompilerTmpEmitterMsg.incrCompilingNum (crackedFsprojs.Length,replyChannel))
64 | replyChannel.Reply()
65 |
66 | let projPaths = crackedFsprojs |> List.map (fun crackedFsproj -> crackedFsproj.ProjPath)
67 | let m = crackedFsprojs.[0].AsList.[1].FSharpProjectOptions
68 | let p =
69 | m.OtherOptions
70 | |> String.concat "\n"
71 | /// from top to bottom
72 | let rec compileByLevel accResults (projLevelMap: Map) = async {
73 | match projLevelMap.IsEmpty with
74 | | false ->
75 | let inProcess =
76 | let maxLevel =
77 | projLevelMap
78 | |> Seq.map (fun pair -> projLevelMap.[pair.Key])
79 | |> Seq.max
80 |
81 | projLevelMap
82 | |> Map.filter (fun projPath level -> level = maxLevel)
83 | |> Seq.map (fun projLevelPair -> projectMap.[projLevelPair.Key]) |> List.ofSeq
84 |
85 | let inProcessProjPaths =
86 | inProcess |> Seq.map (fun crackedProject ->
87 | crackedProject.ProjPath
88 | ) |> List.ofSeq
89 |
90 | let! results = createCompileTask inProcess
91 |
92 | let results =
93 | results
94 | |> Array.tryFind(fun result ->
95 | result.ExitCode <> 0
96 | )
97 | |> function
98 | | Some errorResult ->
99 | [errorResult]
100 |
101 | | None ->
102 | let left = projLevelMap |> Map.filter (fun projPath level ->
103 | not (List.contains projPath inProcessProjPaths)
104 | )
105 |
106 | compileByLevel (accResults @ (List.ofArray results)) left |> Async.RunSynchronously
107 |
108 | return results
109 | | true -> return accResults
110 | }
111 |
112 |
113 | let startTime = DateTime.Now
114 |
115 | let task =
116 | let correnspondingProjLevelMap = model.CrackerFsprojBundleCache.ProjLevelMap |> Map.filter (fun projPath level ->
117 | List.contains projPath projPaths
118 | )
119 |
120 | async {
121 | let! result = compileByLevel [] correnspondingProjLevelMap
122 | compilerTmpEmitterAgent.Post (CompilerTmpEmitterMsg.decrCompilingNum crackedFsprojs.Length)
123 | return result
124 | } |> Async.StartAsTask
125 |
126 | compilerTmpEmitterAgent.Post (CompilerTmpEmitterMsg.addTask (CompilerTask (why, startTime, task)))
127 | return! loop model
128 |
129 | | CompilerMsg.UpdateCache cache ->
130 | return! loop { model with CrackerFsprojBundleCache = cache }
131 |
132 | }
133 |
134 | let entryCrackedFsproj = initialCache.EntryCrackedFsproj
135 |
136 | if compiler.WarmCompile then
137 | inbox.PostAndAsyncReply (fun replyChannel ->
138 | CompilerMsg.CompileProjects (WhyCompile.WarmCompile, [entryCrackedFsproj], replyChannel)
139 | ) |> Async.Start
140 |
141 | loop { CrackerFsprojBundleCache = initialCache }
142 | )
--------------------------------------------------------------------------------
/src/FcsWatch.Binary/Main.fs:
--------------------------------------------------------------------------------
1 | []
2 | module FcsWatch.Binary.Main
3 | open FcsWatch.Core
4 | open System.IO
5 | open FcsWatch.Core.Compiler
6 | open FcsWatch.Core.FcsWatcher
7 | open System
8 | open Extensions
9 | open FSharp.Compiler.SourceCodeServices
10 | open Fake.IO.FileSystemOperators
11 | open FcsWatch.Core.Types
12 |
13 | []
14 | type DevelopmentTarget =
15 | | Debuggable of DebuggingServer.DevelopmentTarget
16 | | AutoReload of AutoReload.DevelopmentTarget
17 |
18 | []
19 | module DevelopmentTarget =
20 | let debuggableProgram = DevelopmentTarget.Debuggable DebuggingServer.DevelopmentTarget.Program
21 | let autoReloadProgram = DevelopmentTarget.AutoReload AutoReload.DevelopmentTarget.Program
22 | let autoReloadPlugin plugin = DevelopmentTarget.AutoReload (AutoReload.DevelopmentTarget.Plugin plugin)
23 | let debuggablePlugin plugin = DevelopmentTarget.Debuggable (DebuggingServer.DevelopmentTarget.Plugin plugin)
24 |
25 | let (|Plugin|Program|) = function
26 | | DevelopmentTarget.AutoReload autoReload ->
27 | match autoReload with
28 | | AutoReload.DevelopmentTarget.Plugin plugin -> Plugin plugin
29 | | AutoReload.DevelopmentTarget.Program _ -> Program
30 |
31 | | DevelopmentTarget.Debuggable debuggable ->
32 | match debuggable with
33 | | DebuggingServer.DevelopmentTarget.Plugin plugin -> Plugin (DebuggingServer.Plugin.asAutoReloadPlugin plugin)
34 | | DebuggingServer.DevelopmentTarget.Program _ -> Program
35 |
36 |
37 | type BinaryConfig =
38 | { LoggerLevel: Logger.Level
39 | DevelopmentTarget: DevelopmentTarget
40 | WorkingDir: string
41 | NoBuild: bool
42 | Framework: string option
43 | Configuration: Configuration
44 | UseEditFiles: bool
45 | Webhook: string option
46 | WarmCompile: bool
47 | AdditionalSwitchArgs : string array }
48 |
49 | with
50 | static member DefaultValue =
51 | { LoggerLevel = Logger.Level.Minimal
52 | DevelopmentTarget = DevelopmentTarget.autoReloadProgram
53 | WorkingDir = Directory.GetCurrentDirectory()
54 | NoBuild = false
55 | Framework = None
56 | Configuration = Configuration.Debug
57 | UseEditFiles = false
58 | WarmCompile = true
59 | Webhook = None
60 | AdditionalSwitchArgs = Array.empty }
61 |
62 |
63 | []
64 | module BinaryConfig =
65 |
66 | let additionalBuildArgs (config: BinaryConfig) =
67 | match config.Configuration with
68 | | Configuration.Release -> ["--configuration"; "Release"]
69 | | Configuration.Debug -> ["--configuration"; "Debug"]
70 |
71 | let asAutoReloadProgramRunningArgs whyRun autoReloadDevelopmentTarget (config: BinaryConfig) : AutoReload.ProgramRunningArgs =
72 | { AdditionalSwitchArgs = List.ofArray config.AdditionalSwitchArgs
73 | Webhook = config.Webhook
74 | DevelopmentTarget = autoReloadDevelopmentTarget
75 | WorkingDir = config.WorkingDir
76 | Framework = config.Framework
77 | AdditionalBuildArgs = additionalBuildArgs config
78 | Configuration = config.Configuration
79 | WhyRun = whyRun }
80 |
81 |
82 | let tryBuildProject projectFile (config: BinaryConfig) =
83 | if not config.NoBuild then
84 | let additionalBuildArgs = additionalBuildArgs config
85 |
86 | match config.DevelopmentTarget with
87 | | DevelopmentTarget.Program _ ->
88 | dotnet "build" ([projectFile] @ additionalBuildArgs) config.WorkingDir
89 |
90 | | DevelopmentTarget.Plugin plugin ->
91 | plugin.Unload()
92 | dotnet "build" ([projectFile] @ additionalBuildArgs) config.WorkingDir
93 |
94 | let binaryFcsWatcher (config: BinaryConfig) entryProjectFile =
95 |
96 | logger <- Logger.create config.LoggerLevel
97 |
98 | let config =
99 | { config with
100 | WorkingDir = Path.GetFullPath(config.WorkingDir)}
101 |
102 | let compiler =
103 | let summary projectPath dll elapsed =
104 | sprintf "Summary:
105 | -- origin: %s
106 | -- dest: %s
107 | -- elapsed: %d milliseconds"
108 | projectPath dll elapsed
109 |
110 | { new ICompiler with
111 | member x.Compile(checker, crackedFsproj) = CrackedFsproj.compile checker crackedFsproj
112 | member x.WarmCompile = config.WarmCompile
113 | member x.Summary (result, elapsed) = summary result.ProjPath result.Dll elapsed}
114 |
115 | let coreConfig: FcsWatch.Core.Config =
116 | { LoggerLevel = config.LoggerLevel
117 | WorkingDir = config.WorkingDir
118 | OtherFlags = [||]
119 | Configuration = config.Configuration
120 | UseEditFiles = config.UseEditFiles }
121 |
122 | let checker = FSharpChecker.Create()
123 |
124 | BinaryConfig.tryBuildProject entryProjectFile config
125 |
126 | match config.DevelopmentTarget with
127 | | DevelopmentTarget.AutoReload autoReload ->
128 |
129 | let programRunningArgs: AutoReload.ProgramRunningArgs =
130 | BinaryConfig.asAutoReloadProgramRunningArgs WhyRun.Run autoReload config
131 |
132 | let compilerTmpEmitter = AutoReload.create programRunningArgs
133 |
134 | let fcsWatcher =
135 | fcsWatcherAndCompilerTmpAgent checker compilerTmpEmitter compiler coreConfig (Some entryProjectFile)
136 | |> fst
137 |
138 | let cache = fcsWatcher.PostAndReply FcsWatcherMsg.GetCache
139 |
140 | AutoReload.CrackedFsproj.tryRun programRunningArgs cache.EntryCrackedFsproj
141 |
142 | fcsWatcher
143 |
144 | | DevelopmentTarget.Debuggable debuggable ->
145 | let compilerTmpEmitter = DebuggingServer.create debuggable
146 | let fcsWatcher, compilerTmpEmitterAgent = fcsWatcherAndCompilerTmpAgent checker compilerTmpEmitter compiler coreConfig (Some entryProjectFile)
147 | DebuggingServer.startServer config.WorkingDir compilerTmpEmitterAgent
148 | fcsWatcher
149 |
150 | let tryKill autoReloadDevelopmentTarget entryCrackedFsproj =
151 | AutoReload.CrackedFsproj.tryKill autoReloadDevelopmentTarget entryCrackedFsproj
152 |
153 | let runFcsWatcher (processExit : System.Threading.Tasks.Task) (config: BinaryConfig) entryProjectFile = async {
154 | let binaryFcsWatcher = binaryFcsWatcher config entryProjectFile
155 | do! processExit |> Async.AwaitTask
156 | let cache = binaryFcsWatcher.PostAndReply FcsWatcherMsg.GetCache
157 | match config.DevelopmentTarget with
158 | | DevelopmentTarget.AutoReload autoReload -> tryKill autoReload cache.EntryCrackedFsproj
159 | | _ -> ()
160 |
161 | logger.Info "Exited fcswatch.exe"
162 | }
163 |
--------------------------------------------------------------------------------
/src/FcsWatch.Binary/DebuggingServer.fs:
--------------------------------------------------------------------------------
1 | []
2 | module FcsWatch.Binary.DebuggingServer
3 | open Fake.IO
4 | open System.Net
5 | open Fake.IO.FileSystemOperators
6 | open System.Text
7 | open FcsWatch.Core
8 | open Suave.Filters
9 | open Suave.Operators
10 | open Suave
11 | open System.Net.Sockets
12 | open FcsWatch.Core.CompilerTmpEmitter
13 | open Extensions
14 | open FcsWatch.Core.Types
15 | open VscodeHelper
16 |
17 | type Plugin =
18 | { Load: unit -> unit
19 | Unload: unit -> unit
20 | Calculate: unit -> unit
21 | TimeDelayAfterUninstallPlugin: int
22 | PluginDebugInfo: PluginDebugInfo }
23 |
24 | []
25 | module Plugin =
26 | let asAutoReloadPlugin (plugin: Plugin): AutoReload.Plugin =
27 | { Load = plugin.Load
28 | Unload = plugin.Unload
29 | Calculate = plugin.Calculate
30 | TimeDelayAfterUninstallPlugin = plugin.TimeDelayAfterUninstallPlugin
31 | PluginDebugInfo = Some plugin.PluginDebugInfo }
32 |
33 | []
34 | type DevelopmentTarget =
35 | | Program
36 | | Plugin of Plugin
37 |
38 |
39 | let private freePort() =
40 | let listener = new TcpListener(IPAddress.Any, 0);
41 | listener.Start()
42 | let port = (listener.LocalEndpoint :?> IPEndPoint).Port
43 | listener.Stop()
44 | port
45 |
46 | let private generateCurlCache freePort root =
47 | let cacheDir = root > ".fake" > "fcswatch"
48 |
49 | let fileName = cacheDir > "port.cache"
50 |
51 | Directory.ensure cacheDir
52 |
53 | let lines =
54 | [ sprintf "url: http://localhost:%d/emitCompilerTmp" freePort
55 | "-f" ]
56 |
57 | File.writeWithEncoding Encoding.ASCII false fileName lines
58 |
59 |
60 | type TmpEmitterMsg = TmpEmitterMsg of replyChannel: AsyncReplyChannel
61 | type TmpEmitterState = CompilerTmpEmitterState list, CompilerResult>
62 |
63 |
64 | []
65 | module internal TmpEmitterState =
66 | open System.Threading
67 |
68 | let tryEmit (developmentTarget: DevelopmentTarget) (tmpEmitterState: TmpEmitterState) =
69 | let emitReplyChannels = tmpEmitterState.CustomState
70 | let commonState = tmpEmitterState.CommonState
71 | let cache = commonState.CrackerFsprojFileBundleCache
72 | logger.Info "tryEmitAction: current emitReplyChannels number is %d" emitReplyChannels.Length
73 |
74 | match commonState.CompilingNumber, emitReplyChannels with
75 | | 0, h::t ->
76 | let replySuccess() = h.Reply (Successful.OK "fcswatch: Ready to debug")
77 |
78 | let replyFailure errorText = h.Reply (RequestErrors.BAD_REQUEST errorText)
79 |
80 | logger.Info "Current valid compier task is %d" commonState.CachedCompilerTasks.Length
81 |
82 | match commonState.CachedCompilerTasks with
83 | | [] ->
84 | replySuccess()
85 |
86 | match developmentTarget with
87 | | DevelopmentTarget.Plugin plugin ->
88 | Thread.Sleep(plugin.PluginDebugInfo.DebuggerAttachTimeDelay)
89 | plugin.Calculate()
90 | | _ -> ()
91 |
92 | tmpEmitterState
93 | | _ ->
94 | let lastTasks =
95 | commonState.CachedCompilerTasks
96 | |> List.groupBy (fun compilerTask ->
97 | compilerTask.Task.Result.[0].ProjPath
98 | )
99 | |> List.map (fun (projPath, compilerTasks) ->
100 | compilerTasks |> List.maxBy (fun compilerTask -> compilerTask.StartTime)
101 | )
102 |
103 | let allResults = lastTasks |> List.collect (fun task -> task.Task.Result)
104 |
105 | match List.tryFind (fun result -> result.ExitCode <> 0) allResults with
106 | | Some result ->
107 | let errorText =
108 | result.Errors
109 | |> Seq.map (fun error -> error.ToString())
110 | |> String.concat "\n"
111 |
112 | replyFailure errorText
113 | { tmpEmitterState with CustomState = [] }
114 |
115 | | None ->
116 |
117 | let projRefersMap = cache.ProjRefersMap
118 |
119 | let projLevelMap = cache.ProjLevelMap
120 |
121 | match developmentTarget with
122 | | DevelopmentTarget.Plugin plugin ->
123 | plugin.Unload()
124 | Thread.Sleep plugin.TimeDelayAfterUninstallPlugin
125 | | _ -> ()
126 |
127 | commonState.CompilerTmp
128 | |> Seq.sortByDescending (fun projPath ->
129 | projLevelMap.[projPath]
130 | )
131 | |> Seq.iter (fun projPath ->
132 | let currentCrackedFsproj = cache.ProjectMap.[projPath]
133 |
134 | CrackedFsproj.copyObjToBin Configuration.Debug currentCrackedFsproj
135 |
136 | let refCrackedFsprojs = projRefersMap.[projPath]
137 |
138 | refCrackedFsprojs |> Seq.sortByDescending (fun refCrackedFsproj ->
139 | projLevelMap.[refCrackedFsproj.ProjPath]
140 | )
141 | |> Seq.iter (CrackedFsproj.copyFileFromRefDllToBin Configuration.Debug projPath)
142 | )
143 |
144 | replySuccess()
145 |
146 | match developmentTarget with
147 | | DevelopmentTarget.Plugin plugin ->
148 | plugin.Load()
149 | plugin.Calculate()
150 | | _ -> ()
151 |
152 | CompilerTmpEmitterState.createEmpty [] cache
153 |
154 | | _ -> tmpEmitterState
155 |
156 | let startServer workingDir (compileTmpEmitterAgent: IMailboxProcessor) =
157 | let root =
158 | File.tryFindRootUpByLaunchJson workingDir
159 | |> Option.defaultValue workingDir
160 |
161 | async {
162 |
163 | let webApp =
164 | let emitCompilerTmp: WebPart =
165 | fun (ctx : HttpContext) ->
166 | async {
167 | let! handler =
168 | /// will finnaly invoke tryEmit
169 | compileTmpEmitterAgent.PostAndAsyncReply TmpEmitterMsg
170 | return! handler ctx
171 | }
172 |
173 | choose [
174 | path "/emitCompilerTmp" >=> emitCompilerTmp
175 | ]
176 | let config =
177 | let freePort = freePort()
178 | generateCurlCache freePort root
179 | let local = Suave.Http.HttpBinding.createSimple HTTP "127.0.0.1" freePort
180 |
181 | { defaultConfig with bindings = [local] }
182 |
183 | startWebServer config webApp
184 | } |> Async.Start
185 |
186 | let create developmentTarget =
187 | { new ICompilerTmpEmitter<_, _, CompilerResult> with
188 | member x.TryEmit(workingDir, state) = TmpEmitterState.tryEmit developmentTarget state
189 | member x.ProcessCustomMsg (customMsg, state) =
190 | let (TmpEmitterMsg replyChannel) = customMsg
191 | { state with
192 | CustomState = replyChannel :: state.CustomState }
193 | |> TmpEmitterState.tryEmit developmentTarget
194 |
195 | member x.CustomInitialState = [] }
196 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/tests/datas/repro-projects/src/Masse.Admin.Tools/Masse.Admin.Tools.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 | Exe
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | PreserveNewest
30 |
31 |
32 |
33 | PreserveNewest
34 |
35 |
36 | PreserveNewest
37 |
38 |
39 | PreserveNewest
40 |
41 |
42 | PreserveNewest
43 |
44 |
45 | PreserveNewest
46 |
47 |
48 | PreserveNewest
49 |
50 |
51 | PreserveNewest
52 |
53 |
54 | PreserveNewest
55 |
56 |
57 |
58 | PreserveNewest
59 |
60 |
61 | PreserveNewest
62 |
63 |
64 | PreserveNewest
65 |
66 |
67 | PreserveNewest
68 |
69 |
70 | PreserveNewest
71 |
72 |
73 | PreserveNewest
74 |
75 |
76 | PreserveNewest
77 |
78 |
79 | PreserveNewest
80 |
81 |
82 | PreserveNewest
83 |
84 |
85 | PreserveNewest
86 |
87 |
88 | PreserveNewest
89 |
90 |
91 | PreserveNewest
92 |
93 |
94 | PreserveNewest
95 |
96 |
97 | PreserveNewest
98 |
99 |
100 | PreserveNewest
101 |
102 |
103 |
104 |
105 | PreserveNewest
106 |
107 |
108 | PreserveNewest
109 |
110 |
111 | PreserveNewest
112 |
113 |
114 | PreserveNewest
115 |
116 |
117 | PreserveNewest
118 |
119 |
120 | PreserveNewest
121 |
122 |
123 | PreserveNewest
124 |
125 |
126 | PreserveNewest
127 |
128 |
129 | PreserveNewest
130 |
131 |
132 | PreserveNewest
133 |
134 |
135 | PreserveNewest
136 |
137 |
138 | PreserveNewest
139 |
140 |
141 | PreserveNewest
142 |
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Run standard fsharp codes in watch mode
2 |
3 | Stable | Prerelease
4 | --- | ---
5 | [](https://www.nuget.org/packages/FCSWatch.Binary/) | [](https://www.nuget.org/packages/FCSWatch.Binary/)
6 |
7 |
8 | MacOS/Linux | Windows
9 | --- | ---
10 | [](https://circleci.com/gh/humhei/FCSWatch) | [](https://ci.appveyor.com/project/ts2fable-imports/FCSWatch)
11 | [](https://circleci.com/gh/humhei/FCSWatch) | [](https://ci.appveyor.com/project/ts2fable-imports/FCSWatch)
12 |
13 |
14 | ---
15 |
16 |
17 | ## Get started
18 | ### From Cli
19 | `dotnet tool install --global fcswatch-cli`
20 |
21 | `fcswatch --project-file yourProjectFile`
22 |
23 | FcsWatch will load your project in autoReload mode by defalut
24 |
25 | ```
26 | USAGE: fcswatch.exe [--help] [--working-dir ] [--project-file ] [--debuggable]
27 | [--logger-level ] [--no-build] [--webhook ] [--send]
28 | [--framework ] [--configuration ]
29 |
30 | OPTIONS:
31 |
32 | --working-dir
33 | Specfic working directory, default is current directory
34 | --project-file
35 | Entry project file, default is exact fsproj file in working dir
36 | --debuggable Enable debuggable in vscode, This will disable auto Reload
37 | --logger-level
38 | Default is Minimal
39 | --no-build --no-build
40 | --webhook send a web hook when program (re)run
41 | --send Equivalent to --webhook http://localhost:9867/update
42 | --framework, -f
43 | The target framework to build for. The default to prefer netcore.
44 | --configuration, -c
45 | Has limitations; See #26 for detail
46 | The configuration to use for building the project. The default is Debug.
47 | --help display this list of options.
48 |
49 | -- --your-custom-args value (pass Addtional args pass to application)
50 | ```
51 |
52 | ### From Fake
53 | 1. Install [fake5](https://fake.build/fake-gettingstarted.html)
54 | 2. Replace build.fsx with below codes
55 | ```fsharp
56 | #r "paket:
57 | source https://api.nuget.org/v3/index.json
58 | nuget Fake.Core.Target = 5.12.0
59 | nuget FcsWatch.Binary //"
60 | #load "./.fake/build.fsx/intellisense.fsx"
61 |
62 | // start build
63 | open Fake.Core
64 | open Fake.IO
65 | open FSharp.Compiler.SourceCodeServices
66 | open FcsWatch.Binary
67 |
68 | Target.create "FcsWatch" (fun _ ->
69 | /// replace it to your entry project file
70 | let projectFile = Path.getFullName "./FcsWatchMiniSample/FcsWatchMiniSample.fsproj"
71 | runFcsWatcher Config.DefaultValue projectFile
72 | )
73 |
74 | Target.runOrDefault "FcsWatch"
75 |
76 | ```
77 | 3. `fake build -t "FcsWatch"`
78 | 4. `Change fs files in YourProject` and save it
79 |
80 |
81 | ## Build From project
82 | * .paket/paket.exe install
83 | * dotnet build FCSWatch.sln
84 |
85 | ## File structure
86 | ### FcsWatch.Core
87 | The core library (Include a lots of common logic
88 | e.g `project cracker`, `file watcher`, mailbox group for concurrrent, and so on )
89 |
90 | ### FcsWatch.Binary (Ref FcsWatch.Core)
91 | It compile fsharp codes to .dll and .pdb
92 | And then stop the whole application and replace the `.dll` and `.pdb` and then rerun application
93 |
94 | ### FcsWatch-Porta (Ref FcsWatch.Core)
95 | It is ported from [FSharp.Compiler.PortaCode](https://github.com/fsprojects/FSharp.Compiler.PortaCode)
96 | It sends a webhook to the host program
97 | And then, the host program can replace its logic
98 |
99 |
100 | ## Debug in vscode(only when AutoReload is false)
101 |
102 | ### Play around
103 | #### From source code interaction test
104 | * git clone https://github.com/humhei/FCSWatch.git
105 | * fcswatch --project-file "fullPath to \FCSWatch\tests\datas\TestProject\TestProject.fsproj" --debuggable
106 | * modify fs files in any of TestProject,TestLib2,TestLib1
107 | * Set breakpoint in any of TestProject,TestLib2,TestLib1
108 | * F5 Debug `Launch TestProject`
109 | * modify fs files in any of TestProject,TestLib2,TestLib1
110 | * (Optional) add new fs file in any of TestProject,TestLib2,TestLib1
111 | * Relaunch Debugger
112 |
113 |
114 | ### Launch debugging in vscode
115 | You can also launch debugging when running in watch mode
116 | ```
117 | {
118 | "name": "launch TestProject",
119 | "type": "coreclr",
120 | "request": "launch",
121 | "preLaunchTask": "emitCompilerTmp",
122 | "program": "${workspaceFolder}/YourProject/bin/Debug/targetFramwork/YourProject.exe",
123 | },
124 | /// send a http request to copy dlls in obj to bin
125 | {
126 | "label": "emitCompilerTmp",
127 | "command": "curl",
128 | "args": [
129 | "--config",
130 | ".fake/fcswatch/port.cache"
131 | ],
132 | "presentation": {
133 | "reveal": "silent"
134 | }
135 | },
136 | },
137 | ```
138 |
139 | When you are debugging files,watch mode still take effect
140 |
141 |
142 | ## Plugin mode
143 | e.g.: excelDna sample
144 | vscode launch.json setting
145 | ```
146 | {
147 | "name": "Attach Excel",
148 | "type": "clr",
149 | "request": "attach",
150 | "preLaunchTask": "emitCompilerTmp",
151 | /// should be write automatically by script
152 | "processId": 14876
153 | },
154 | ```
155 |
156 | build.fsx setting
157 | ```fsharp
158 | open FcsWatch.Binary
159 |
160 | let app =
161 | Process.GetProcesses()
162 | |> Array.tryFind (fun proc -> proc.ProcessName = "EXCEL")
163 | |> function
164 | | Some proc -> Marshal.GetActiveObject("Excel.Application")
165 | | None ->
166 | failwithf "Please manual open excel" projectName
167 | :?> Application
168 |
169 | let procId = User32.getPidFromHwnd app.Hwnd
170 |
171 | /// trigger when file changed was detected
172 | /// and (re)load debugger (after emit cache)
173 | let installPlugin() =
174 | addIn.Installed <- true
175 | Trace.trace "installed plugin"
176 |
177 | /// trigger when file changed was detected
178 | /// and (re)load debugger (before emit cache)
179 | let unInstall() =
180 | addIn.Installed <- false
181 | Trace.trace "unInstalled plugin"
182 |
183 | /// trigger when (re)load debugger (after installPlugin())
184 | let calculate() =
185 | Trace.trace "calculate worksheet"
186 | worksheet.Calculate()
187 |
188 |
189 | let pluginDebugInfo: PluginDebugInfo =
190 | {
191 | /// Thread sleed to wait debugger attached.
192 | /// Trigger when file changed was not detected
193 | /// and reload debugger
194 | DebuggerAttachTimeDelay = 2000
195 | // pid write to .vscode/launch.json
196 | Pid = procId
197 | VscodeLaunchConfigurationName = "Attach Excel" }
198 |
199 | let plugin : DebuggingServer.Plugin =
200 | { Load = install
201 | Unload = unInstall
202 | Calculate = calculate
203 | TimeDelayAfterUninstallPlugin = 500
204 | PluginDebugInfo = pluginDebugInfo }
205 |
206 | let config =
207 | {Config.DefaultValue with
208 | DevelopmentTarget = DevelopmentTarget.autoReloadPlugin plugin }
209 |
210 | runFcsWatcher config projectFile
211 |
212 | ```
213 |
214 | ## Why?
215 | why not use dotnet watch:
216 | 1. dotnet watch reference all dlls every time (which will take at least 3000ms?) (while fcs hold dlls in runtime cache)
217 | 2. not easy to debug when you are using dotnet watch
218 |
219 |
220 | 
221 |
--------------------------------------------------------------------------------
/src/FcsWatch.Core/CompilerTmpEmitter.fs:
--------------------------------------------------------------------------------
1 | module FcsWatch.Core.CompilerTmpEmitter
2 | open Types
3 | open System.Threading
4 |
5 |
6 | []
7 | type CompilerTmpEmitterMsg<'Result when 'Result :> ICompilerOrCheckResult> =
8 | | IncrCompilingNum of int * replyChannel: AsyncReplyChannel
9 | | DecrCompilingNum of int
10 | | AddTmp of string (*proj path*)
11 | | AddTask of CompilerTask<'Result>
12 | | UpdateCache of CrackedFsprojBundleCache
13 | /// for blackbox test
14 | | WaitCompiled of AsyncReplyChannel
15 |
16 | []
17 | type CompilerTmpEmitterMsg<'CustomMsg, 'Result when 'Result :> ICompilerOrCheckResult> =
18 | | CustomMsg of 'CustomMsg
19 | | CommonMsg of CompilerTmpEmitterMsg<'Result>
20 |
21 | []
22 | module CompilerTmpEmitterMsg =
23 | let addTmp projPath =
24 | (CompilerTmpEmitterMsg<_, _>.CommonMsg (CompilerTmpEmitterMsg<_>.AddTmp projPath))
25 |
26 | let incrCompilingNum (number, replyChannel) =
27 | (CompilerTmpEmitterMsg<_, _>.CommonMsg (CompilerTmpEmitterMsg<_>.IncrCompilingNum (number, replyChannel)))
28 |
29 | let decrCompilingNum number =
30 | (CompilerTmpEmitterMsg<_, _>.CommonMsg (CompilerTmpEmitterMsg<_>.DecrCompilingNum number))
31 |
32 | let addTask task =
33 | (CompilerTmpEmitterMsg<_, _>.CommonMsg (CompilerTmpEmitterMsg<_>.AddTask task))
34 |
35 | let updateCache cache =
36 | (CompilerTmpEmitterMsg<_, _>.CommonMsg (CompilerTmpEmitterMsg<_>.UpdateCache cache))
37 |
38 | let internal customMsg msg =
39 | (CompilerTmpEmitterMsg<_, _>.CustomMsg msg)
40 |
41 |
42 | /// for blackbox test
43 | let internal waitCompiled replyChannel =
44 | (CompilerTmpEmitterMsg<_, _>.CommonMsg (CompilerTmpEmitterMsg<_>.WaitCompiled replyChannel))
45 |
46 | let msgName = function
47 | | CompilerTmpEmitterMsg.IncrCompilingNum _ -> "IncrCompilingNum"
48 | | CompilerTmpEmitterMsg.DecrCompilingNum _ -> "DecrCompilingNum"
49 | | CompilerTmpEmitterMsg.AddTmp _ -> "AddTmp"
50 | | CompilerTmpEmitterMsg.AddTask _ -> "AddTask"
51 | | CompilerTmpEmitterMsg.UpdateCache _ -> "UpdateCache"
52 | | CompilerTmpEmitterMsg.WaitCompiled _ -> "WaitCompiled"
53 |
54 | type CompilerTmpEmitterState<'Result when 'Result :> ICompilerOrCheckResult> =
55 | { CompilingNumber: int
56 | /// proj paths
57 | CompilerTmp: Set
58 | CachedCompilerTasks: CompilerTask<'Result> list
59 | /// used for test only
60 | LastestIncredNum: int
61 | CrackerFsprojFileBundleCache: CrackedFsprojBundleCache
62 | AccumWaitCompiledReplyChannels: AsyncReplyChannel list}
63 |
64 | type CompilerTmpEmitterState<'CustomState, 'Result when 'Result :> ICompilerOrCheckResult> =
65 | { CustomState: 'CustomState
66 | CommonState: CompilerTmpEmitterState<'Result> }
67 |
68 | []
69 | module CompilerTmpEmitterState =
70 |
71 | let createCommonEmpty cache =
72 | { CompilingNumber = 0
73 | CompilerTmp = Set.empty
74 | CachedCompilerTasks = []
75 | LastestIncredNum = 0
76 | CrackerFsprojFileBundleCache = cache
77 | AccumWaitCompiledReplyChannels = [] }
78 |
79 | let createEmpty customState cache =
80 | { CommonState = createCommonEmpty cache
81 | CustomState = customState }
82 |
83 |
84 | let setCompilingNumber number state =
85 | { state with
86 | CommonState =
87 | {state.CommonState with CompilingNumber = number }}
88 |
89 |
90 | let internal tryReplyCompiled (state: CompilerTmpEmitterState<_>) =
91 | if state.CompilingNumber = 0 && state.AccumWaitCompiledReplyChannels.Length > 0
92 | then
93 | state.AccumWaitCompiledReplyChannels |> List.iter (fun replyChannel -> replyChannel.Reply state.LastestIncredNum)
94 | createCommonEmpty state.CrackerFsprojFileBundleCache
95 | else
96 | state
97 |
98 | let processMsg (msg: CompilerTmpEmitterMsg<_>) (state: CompilerTmpEmitterState<_>) =
99 | let newState =
100 | match msg with
101 |
102 | | CompilerTmpEmitterMsg.DecrCompilingNum number ->
103 | let compilingNumber = state.CompilingNumber - number
104 | let newState = {state with CompilingNumber = compilingNumber}
105 |
106 | assert (newState.CompilingNumber >= 0)
107 | newState
108 |
109 | | CompilerTmpEmitterMsg.IncrCompilingNum (number, replyChannel) ->
110 | let compilingNumber = state.CompilingNumber + number
111 | let newState =
112 | {state with
113 | CompilingNumber = compilingNumber
114 | LastestIncredNum = compilingNumber }
115 |
116 | replyChannel.Reply()
117 |
118 | newState
119 |
120 | | CompilerTmpEmitterMsg.AddTmp projectFile ->
121 | let newCompilerTmp = state.CompilerTmp.Add projectFile
122 |
123 | {state with CompilerTmp = newCompilerTmp }
124 |
125 | | CompilerTmpEmitterMsg.AddTask task ->
126 | {state with CachedCompilerTasks = task :: state.CachedCompilerTasks}
127 |
128 | | CompilerTmpEmitterMsg.UpdateCache cache ->
129 | {state with CrackerFsprojFileBundleCache = cache}
130 |
131 | | CompilerTmpEmitterMsg.WaitCompiled replyChannel ->
132 | let newState = { state with AccumWaitCompiledReplyChannels = replyChannel :: state.AccumWaitCompiledReplyChannels }
133 | tryReplyCompiled newState
134 |
135 | let msgName = CompilerTmpEmitterMsg.msgName msg
136 | logger.Info "compilerTmpEmitter agent receive message %s,current compiling number is %d" msgName newState.CompilingNumber
137 |
138 | newState
139 |
140 | type ICompilerTmpEmitter<'CustomMsg, 'CustomState,'Result when 'Result :> ICompilerOrCheckResult> =
141 | abstract member ProcessCustomMsg: customMsg: 'CustomMsg * state: CompilerTmpEmitterState<'CustomState,'Result> -> CompilerTmpEmitterState<'CustomState,'Result>
142 | abstract member TryEmit: workingDir: string * state: CompilerTmpEmitterState<'CustomState,'Result> -> CompilerTmpEmitterState<'CustomState,'Result>
143 | abstract member CustomInitialState: 'CustomState
144 |
145 | let compilerTmpEmitterAgent workingDir (compilerTmpEmitter: ICompilerTmpEmitter<_, _, _>) (initialCache: CrackedFsprojBundleCache) = MailboxProcessor>.Start(fun inbox ->
146 | inbox.Error.Add(fun error -> logger.Error "%A" error)
147 |
148 | let rec loop state = async {
149 |
150 | let! msg = inbox.Receive()
151 | match msg with
152 | | CompilerTmpEmitterMsg.CustomMsg customMsg ->
153 | let newState = compilerTmpEmitter.ProcessCustomMsg (customMsg, state)
154 | return! loop newState
155 | | CompilerTmpEmitterMsg.CommonMsg commonMsg ->
156 |
157 | let commonProcessedState =
158 | { state with
159 | CommonState = CompilerTmpEmitterState.processMsg commonMsg state.CommonState }
160 |
161 | match commonMsg with
162 | | CompilerTmpEmitterMsg.DecrCompilingNum _ ->
163 |
164 | let resetWaitCompiled state =
165 | { state with
166 | CommonState =
167 | { state.CommonState
168 | with
169 | LastestIncredNum = commonProcessedState.CommonState.LastestIncredNum
170 | AccumWaitCompiledReplyChannels = commonProcessedState.CommonState.AccumWaitCompiledReplyChannels } }
171 |
172 | let replyWaitCompiled state =
173 | { state with
174 | CommonState = CompilerTmpEmitterState.tryReplyCompiled state.CommonState }
175 |
176 | let newState =
177 | compilerTmpEmitter.TryEmit(workingDir, commonProcessedState)
178 | |> resetWaitCompiled
179 | |> replyWaitCompiled
180 |
181 |
182 | return! loop newState
183 |
184 | | _ -> return! loop commonProcessedState
185 | }
186 | loop (CompilerTmpEmitterState.createEmpty compilerTmpEmitter.CustomInitialState initialCache)
187 | )
188 |
189 |
190 |
191 |
192 |
--------------------------------------------------------------------------------
/src/FcsWatch.Binary/AutoReload.fs:
--------------------------------------------------------------------------------
1 |
2 | namespace FcsWatch.Binary
3 | open Fake.Core
4 | open FcsWatch.Core
5 | open Types
6 | open FcsWatch.Core.CrackedFsproj
7 | open System.Diagnostics
8 | open System.Collections.Concurrent
9 | open Fake.DotNet
10 | open FcsWatch.Binary.Helpers
11 | open Fake.IO
12 | open FcsWatch.Core.CompilerTmpEmitter
13 | open Extensions
14 | open System.Threading
15 | open System.Net
16 | open System.Text
17 |
18 |
19 | []
20 | type WhyRun =
21 | | Rerun of dllUpdates: string list
22 | | Run
23 |
24 | type PluginDebugInfo =
25 | { DebuggerAttachTimeDelay: int
26 | Pid: int
27 | VscodeLaunchConfigurationName: string }
28 |
29 | []
30 | module PluginDebugInfo =
31 | open VscodeHelper
32 |
33 |
34 | let writePidForPlugin workingDir (pluginDebugInfo: PluginDebugInfo) =
35 | match File.tryFindLaunchJsonUp workingDir with
36 | | Some file ->
37 |
38 | let launch = Launch.read file
39 | Launch.writePid pluginDebugInfo.VscodeLaunchConfigurationName pluginDebugInfo.Pid launch
40 | |> Launch.write file
41 | | None -> logger.Important "Doesn't exists .vscode/launch.json, If you want write attachable pid automatically to launch.json, Please write it first"
42 |
43 |
44 |
45 | []
46 | module AutoReload =
47 |
48 | type TmpEmitterMsg = unit
49 | type TmpEmitterState = CompilerTmpEmitterState
50 |
51 |
52 |
53 | type Plugin =
54 | { Load: unit -> unit
55 | Unload: unit -> unit
56 | Calculate: unit -> unit
57 | TimeDelayAfterUninstallPlugin: int
58 | /// sometimes i want work both in autoReload + debuggable
59 | PluginDebugInfo: PluginDebugInfo option }
60 |
61 |
62 | []
63 | type DevelopmentTarget =
64 | | Program
65 | | Plugin of Plugin
66 |
67 | type ProgramRunningArgs =
68 | { Webhook: string option
69 | AdditionalBuildArgs: string list
70 | AdditionalSwitchArgs: string list
71 | DevelopmentTarget: DevelopmentTarget
72 | WorkingDir: string
73 | Framework: string option
74 | Configuration: Configuration
75 | WhyRun: WhyRun }
76 |
77 | []
78 | module ProgramRunningArgs =
79 |
80 | let addtionalRunArgs (crackedFsproj: CrackedFsproj) (args: ProgramRunningArgs) =
81 | let framework =
82 | match args.Framework with
83 | | Some f -> ["--framework"; f]
84 | | None -> ["--framework"; crackedFsproj.PreferFramework]
85 |
86 | if args.AdditionalSwitchArgs.Length = 0
87 | then
88 | args.AdditionalBuildArgs @ framework
89 | else
90 | args.AdditionalBuildArgs @ framework @ ["--"] @ args.AdditionalSwitchArgs
91 |
92 |
93 | let private runningProjects = new ConcurrentDictionary()
94 |
95 | []
96 | module internal CrackedFsproj =
97 |
98 | let private dotnetTool =
99 | [DotNet.Options.Create().DotNetCliPath]
100 | |> Args.toWindowsCommandLine
101 |
102 | []
103 | module Process =
104 | let start tool args workingDir =
105 | let startInfo = new ProcessStartInfo();
106 | let args = Args.toWindowsCommandLine args
107 | startInfo.WorkingDirectory <- workingDir
108 | startInfo.Arguments <- args
109 | startInfo.FileName <- tool
110 | logger.ImportantGreen "%s %s" tool args
111 | Process.Start(startInfo)
112 |
113 | let tryRun (args: ProgramRunningArgs) (crackedFsproj: CrackedFsproj) =
114 |
115 | match (args.DevelopmentTarget: DevelopmentTarget) with
116 | | DevelopmentTarget.Program ->
117 | match crackedFsproj.ProjectTarget with
118 | | ProjectTarget.Exe ->
119 |
120 | let dotnetArgs =
121 | ["run";"--project"; crackedFsproj.ProjPath; "--no-build"; "--no-restore"]
122 |
123 |
124 | runningProjects.GetOrAdd (crackedFsproj.ProjPath,fun _ ->
125 | let proc =
126 | Process.start
127 | dotnetTool
128 | (dotnetArgs @ (ProgramRunningArgs.addtionalRunArgs crackedFsproj args))
129 | args.WorkingDir
130 |
131 | match args.Webhook with
132 | | Some webhook ->
133 | let json = Newtonsoft.Json.JsonConvert.SerializeObject args.WhyRun
134 | use webClient = new WebClient(Encoding = Encoding.UTF8)
135 | logger.Important "SENDING TO WEBHOOK... " // : <<<%s>>>... --> %s" json.[0 .. min (json.Length - 1) 100] hook
136 | let resp = webClient.UploadString (webhook,"Put",json)
137 | logger.Important "RESP FROM WEBHOOK: %s" resp
138 |
139 | | None -> ()
140 |
141 | proc
142 |
143 |
144 | )
145 | |> ignore
146 |
147 | | ProjectTarget.Library ->
148 | failwith "project is a library, autoReload is ture, development target is program;"
149 |
150 | | DevelopmentTarget.Plugin plugin ->
151 | plugin.Load()
152 |
153 | let tryReRun args (crackedFsproj: CrackedFsproj) (dllUpdates: string list) =
154 | let args = { args with WhyRun = WhyRun.Rerun dllUpdates }
155 | tryRun args (crackedFsproj: CrackedFsproj)
156 |
157 |
158 | let tryKill developmentTarget (crackedFsproj: CrackedFsproj) =
159 | match developmentTarget with
160 | | DevelopmentTarget.Plugin plugin ->
161 | plugin.Unload()
162 | Thread.Sleep plugin.TimeDelayAfterUninstallPlugin
163 | | DevelopmentTarget.Program ->
164 | match runningProjects.TryRemove (crackedFsproj.ProjPath) with
165 | | true, proc ->
166 | if not proc.HasExited
167 | then
168 | proc.KillTree()
169 | logger.InfoGreen "Killed process %d" proc.Id
170 | | false, _ ->
171 | failwithf "Cannot remove %s from running projects" crackedFsproj.ProjPath
172 |
173 |
174 | []
175 | module internal TmpEmitterState =
176 |
177 | let tryEmit args (state: TmpEmitterState) =
178 | let commonState = state.CommonState
179 | let cache = commonState.CrackerFsprojFileBundleCache
180 |
181 | match commonState.CompilingNumber with
182 | | 0 ->
183 |
184 | logger.Info "Current cached compier task is %d" commonState.CachedCompilerTasks.Length
185 |
186 | match commonState.CachedCompilerTasks with
187 | | [task] when task.Why = WhyCompile.WarmCompile -> state
188 | | _ ->
189 | let lastTasks =
190 | commonState.CachedCompilerTasks
191 | |> List.groupBy (fun compilerTask ->
192 | compilerTask.Task.Result.[0].ProjPath
193 | )
194 | |> List.map (fun (projPath, compilerTasks) ->
195 | compilerTasks |> List.maxBy (fun compilerTask -> compilerTask.StartTime)
196 | )
197 |
198 |
199 | let allResults: CompilerResult list = lastTasks |> List.collect (fun task -> task.Task.Result)
200 |
201 | match List.tryFind (fun (result: CompilerResult) -> result.ExitCode <> 0) allResults with
202 | | Some result -> state
203 |
204 | | None ->
205 |
206 | let projRefersMap = cache.ProjRefersMap
207 |
208 | let projLevelMap = cache.ProjLevelMap
209 |
210 | CrackedFsproj.tryKill args.DevelopmentTarget cache.EntryCrackedFsproj
211 |
212 | commonState.CompilerTmp
213 | |> Seq.sortByDescending (fun projPath ->
214 | projLevelMap.[projPath]
215 | )
216 | |> Seq.iter (fun projPath ->
217 | let currentCrackedFsproj = cache.ProjectMap.[projPath]
218 |
219 | CrackedFsproj.copyObjToBin args.Configuration currentCrackedFsproj
220 |
221 | let refCrackedFsprojs = projRefersMap.[projPath]
222 |
223 | refCrackedFsprojs |> Seq.sortByDescending (fun refCrackedFsproj ->
224 | projLevelMap.[refCrackedFsproj.ProjPath]
225 | )
226 | |> Seq.iter (CrackedFsproj.copyFileFromRefDllToBin args.Configuration projPath)
227 |
228 |
229 | )
230 |
231 | let updatedDlls = allResults |> List.map (fun r -> r.Dll)
232 | CrackedFsproj.tryReRun args cache.EntryCrackedFsproj updatedDlls
233 |
234 | CompilerTmpEmitterState.createEmpty 0 cache
235 | | _ ->
236 | state
237 |
238 |
239 | let create args =
240 | { new ICompilerTmpEmitter with
241 | member x.TryEmit(_, state) = TmpEmitterState.tryEmit args state
242 | member x.ProcessCustomMsg (_, state) = state
243 | member x.CustomInitialState = 0 }
244 |
--------------------------------------------------------------------------------
/tests/FcsWatch.Tests/Tests.fs:
--------------------------------------------------------------------------------
1 | module FcsWatch.Tests.Tests
2 | open Expecto
3 | open System.IO
4 | open Fake.IO
5 | open Fake.IO.FileSystemOperators
6 | open System.Threading
7 | open FcsWatch.Core.Types
8 | open FcsWatch.Core.FcsWatcher
9 | open FcsWatch.Tests.Types
10 | open FcsWatch.Core
11 | open FcsWatch.Binary
12 | open Suave
13 | open Suave.Operators
14 | open Suave.Filters
15 |
16 | let pass() = Expect.isTrue true "passed"
17 | let fail() = Expect.isTrue false "failed"
18 |
19 | let root = __SOURCE_DIRECTORY__ > "../../"
20 |
21 | let datas = Path.getDirectory(__SOURCE_DIRECTORY__) > "datas"
22 |
23 | let entryProjDir = datas > "TestProject"
24 |
25 | let entryProjPath = entryProjDir > "TestProject.fsproj"
26 |
27 | let testProjPath = datas > @"TestLib2/TestLib2.fsproj"
28 |
29 | let testSourceFile1 = datas > @"TestLib2/Library.fs"
30 |
31 | let testSourceFile2 = datas > @"TestLib2/Library2.fs"
32 |
33 | let testSourceFileAdded = datas > @"TestLib2/Added.fs"
34 |
35 | let testSourceFile1InTestLib = datas > @"TestLib1/Library.fs"
36 |
37 | let testContentFile = datas > @"TestProject/Content.html"
38 |
39 | let expectCompilerNumber excepted (CompilerNumber compilerNumber) =
40 | Expect.equal excepted compilerNumber (sprintf "expected compiler number %d,while current compiler number is %d" excepted compilerNumber)
41 |
42 |
43 | let makeFileChange fullPath : FileChange =
44 | let fullPath = Path.getFullName fullPath
45 |
46 | { FullPath = fullPath
47 | Name = Path.GetFileName fullPath
48 | Status = FileStatus.Changed }
49 |
50 | let makeSourceFileChanges fullPaths =
51 | FcsWatcherMsg.DetectSourceFileChanges (List.map makeFileChange fullPaths)
52 |
53 | let makeProjectFileChanges fullPaths =
54 | FcsWatcherMsg.DetectProjectFileChanges (List.map makeFileChange fullPaths)
55 |
56 |
57 | let createWatcher (config: BinaryConfig) =
58 | lazy
59 | let config =
60 | { config with
61 | WorkingDir = root
62 | LoggerLevel = Logger.Level.Debug
63 | WarmCompile = false }
64 |
65 | binaryFcsWatcher config entryProjPath
66 |
67 | let testSourceFilesChanged (watcher: Lazy>) sourceFiles expectedCompilerNumber =
68 | watcher.Value.PostAndReply (makeSourceFileChanges sourceFiles)
69 | watcher.Value.PostAndReply FcsWatcherMsg.WaitCompiled
70 | |> expectCompilerNumber expectedCompilerNumber
71 |
72 | let programTests =
73 | let watcher = createWatcher { BinaryConfig.DefaultValue with DevelopmentTarget = DevelopmentTarget.debuggableProgram }
74 |
75 | testList "program tests" [
76 |
77 | testCase "change file in TestLib2/Library.fs will trigger compiling" <| fun _ ->
78 | /// TestLib2/Library.fs
79 | testSourceFilesChanged watcher [testSourceFile1] 1
80 |
81 | testCase "change multiple file in TestLib2 will trigger compiling" <| fun _ ->
82 | /// (TestLib2/Library.fs + TestLib2/Library2.fs)
83 | testSourceFilesChanged watcher [testSourceFile1; testSourceFile2] 1
84 |
85 | testCase "change file in TestLib1 and TestLib2 will trigger compiling" <| fun _ ->
86 | /// (TestLib2/*.fs) + (TestLib1/*.fs)
87 | testSourceFilesChanged watcher [testSourceFile1; testSourceFile2; testSourceFile1InTestLib] 2
88 |
89 | testCase "add fs file in fsproj will update watcher" <| fun _ ->
90 | try
91 | Fsproj.addFileToProject "Added.fs" testProjPath
92 |
93 | Thread.Sleep(1000)
94 | testSourceFilesChanged watcher [testSourceFileAdded] 1
95 |
96 | finally
97 | Fsproj.removeFileFromProject "Added.fs" testProjPath
98 |
99 | testCase "change html content file in TestProject will trigger compiling" <| fun _ ->
100 | /// TestProject/Content.html
101 | testSourceFilesChanged watcher [testContentFile] 1
102 | ]
103 |
104 |
105 | let pluginTests =
106 | let watcher =
107 | let config =
108 | let installPlugin() = printfn "install plugin"
109 |
110 | let unInstallPlugin() = printfn "uninstall plugin"
111 |
112 | let plugin: AutoReload.Plugin =
113 | { Load = installPlugin
114 | Unload = unInstallPlugin
115 | Calculate = (fun _ ->
116 | Thread.Sleep(100)
117 | printf "Calculate" )
118 | TimeDelayAfterUninstallPlugin = 500
119 | PluginDebugInfo = None }
120 |
121 | {BinaryConfig.DefaultValue with DevelopmentTarget = DevelopmentTarget.autoReloadPlugin plugin }
122 |
123 | createWatcher config
124 |
125 |
126 | testList "plugin Tests" [
127 | testCase "in plugin mode " <| fun _ ->
128 | /// TestLib2/Library.fs
129 | testSourceFilesChanged watcher [testSourceFile1] 1
130 | ]
131 |
132 |
133 |
134 | let webhookTests =
135 | let watcher =
136 | createWatcher {
137 | BinaryConfig.DefaultValue with
138 | DevelopmentTarget = DevelopmentTarget.autoReloadProgram
139 | Configuration = Configuration.Release
140 |
141 | Webhook = Some "http://localhost:9867/update" }
142 |
143 |
144 |
145 |
146 | testList "webhook tests" [
147 | testCase "can send webhook and recieve it " <| fun _ ->
148 | let cts = new CancellationTokenSource()
149 | let suaveConfig =
150 | { defaultConfig with
151 | bindings = [ HttpBinding.createSimple HTTP "127.0.0.1" 9867 ]
152 | bufferSize = 2048
153 | cancellationToken = cts.Token }
154 |
155 | let mutable runReasons = []
156 |
157 | let webApp =
158 | choose
159 | [ path "/update" >=>
160 | (fun ctx ->
161 | let whyRun =
162 | Newtonsoft.Json.JsonConvert.DeserializeObject(
163 | ctx.request.rawForm
164 | |> System.Text.ASCIIEncoding.UTF8.GetString)
165 | runReasons <- runReasons @ [whyRun]
166 | Successful.OK "recieved web hook" ctx
167 | )
168 | ]
169 |
170 | let listening, server = startWebServerAsync suaveConfig webApp
171 | Async.Start server
172 |
173 | /// TestLib2/Library.fs
174 | testSourceFilesChanged watcher [testSourceFile1] 1
175 |
176 | let cache = watcher.Value.PostAndReply FcsWatcherMsg.GetCache
177 |
178 | cts.Cancel()
179 | tryKill AutoReload.DevelopmentTarget.Program cache.EntryCrackedFsproj
180 |
181 | match runReasons with
182 | | [runReason; rerunReason] ->
183 |
184 | match runReason, rerunReason with
185 | | WhyRun.Run, WhyRun.Rerun [file] ->
186 | Expect.isTrue (file.EndsWith("TestLib2.dll")) "can receive list of updated dlls"
187 |
188 | | _ -> failwith "expect (run + rerun) when recieve webhook"
189 |
190 | | _ -> failwith "expect (run + rerun) when recieve webhook"
191 |
192 |
193 | ]
194 |
195 | let functionTests =
196 |
197 | let testObjRefOnly configuration = async {
198 | let! fullCracekdFsproj, _ =
199 | FullCrackedFsproj.create (FullCrackedFsprojBuilder.Project {OtherFlags = [||]; File = entryProjPath; Configuration = configuration})
200 |
201 | let otherOptions =
202 | fullCracekdFsproj.Value.AsList |> Seq.collect (fun singleTargetCrackedFsproj ->
203 | singleTargetCrackedFsproj.FSharpProjectOptions.OtherOptions
204 | ) |> List.ofSeq
205 |
206 | let configurationText = Configuration.name configuration
207 |
208 | let p1 =
209 | otherOptions
210 | |> Seq.exists (fun option ->
211 | option.Contains (sprintf @"\bin\%s\" configurationText)
212 | || option.Contains (sprintf @"/bin/%s/" configurationText)
213 | )
214 | |> not
215 |
216 | let p2 =
217 | otherOptions
218 | |> Seq.exists (fun option ->
219 | option.Contains (sprintf @"\obj\%s\" configurationText)
220 | || option.Contains (sprintf @"/obj/%s/" configurationText)
221 | )
222 |
223 | if p1 && p2 then pass()
224 | else fail()
225 | }
226 |
227 |
228 | testList "functionTests"
229 | [
230 | /// "bin ref may be locked by program
231 | testCaseAsync "obj ref only when debug" <| (testObjRefOnly Configuration.Debug)
232 |
233 | /// "bin ref may be locked by program
234 | testCaseAsync "obj ref only when release" <| (testObjRefOnly Configuration.Release)
235 |
236 | testCase "EasyGetAllFsProjects for complex projects" <| fun _ ->
237 | /// no exception should be threw in unix
238 | /// https://github.com/humhei/FCSWatch/issues/19
239 | FullCrackedFsproj.easyGetAllProjPaths (datas > "repro-projects\src\Masse.API\Masse.API.fsproj")
240 | |> ignore
241 |
242 | /// https://github.com/humhei/FCSWatch/issues/30
243 | testCaseAsync "Map project Other options --doc:path to --doc:fullPath and -o:path to -o:fullPath" <| async {
244 | let! fullCracekdFsproj, _ =
245 | FullCrackedFsproj.create (FullCrackedFsprojBuilder.Project {OtherFlags = [||]; File = entryProjPath; Configuration = Configuration.Debug})
246 |
247 | let otherOptions =
248 | fullCracekdFsproj.Value.AsList |> Seq.collect (fun singleTargetCrackedFsproj ->
249 | singleTargetCrackedFsproj.FSharpProjectOptions.OtherOptions
250 | ) |> List.ofSeq
251 |
252 | let testByPrefix (prefix: string) =
253 | Expect.all otherOptions (fun ops ->
254 | let index = ops.IndexOf prefix
255 | if index <> -1 then
256 | let path = ops.Substring(index + prefix.Length)
257 | Path.IsPathRooted path
258 | else true
259 | ) "pass"
260 |
261 | testByPrefix "--doc:"
262 | testByPrefix "-o:"
263 | testByPrefix "-output:"
264 | }
265 |
266 | ]
--------------------------------------------------------------------------------
/FCSWatch.FPublisher.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28307.489
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3D12606D-9919-4874-A10C-E37F1CB84CBC}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{43FBDDAF-4E0A-4D82-BCB2-002472E7F170}"
9 | EndProject
10 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FcsWatch.Tests", "tests\FcsWatch.Tests\FcsWatch.Tests.fsproj", "{14A80081-D97A-405D-9A49-C686ABF00842}"
11 | EndProject
12 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "fcswatch-cli", "src\fcswatch-cli\fcswatch-cli.fsproj", "{BB0051D2-8692-449E-B20F-6DBD605C7553}"
13 | EndProject
14 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FcsWatch.Binary", "src\FcsWatch.Binary\FcsWatch.Binary.fsproj", "{1661E77C-1DD4-4920-9345-C86D04B37DD6}"
15 | EndProject
16 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FcsWatch.Core", "src\FcsWatch.Core\FcsWatch.Core.fsproj", "{70B2741D-D9AC-47B2-9D0C-78478562C6D5}"
17 | EndProject
18 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FsLive.Porta.Cli.Tests", "tests\FsLive.Porta.Cli.Tests\FsLive.Porta.Cli.Tests.fsproj", "{A9F69171-7544-4029-A2EA-4E6FF49D2E1C}"
19 | EndProject
20 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FcsWatch.Porta", "src\FcsWatch.Porta\FcsWatch.Porta.fsproj", "{0DDF9662-A5E5-41B2-ADF2-D38447312611}"
21 | EndProject
22 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FcsWatch.Porta.Interpreter", "src\FcsWatch.Porta.Interpreter\FcsWatch.Porta.Interpreter.fsproj", "{EC522CB8-5E90-4F4E-9059-62DAB763A544}"
23 | EndProject
24 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "fslive-cli", "src\fslive-cli\fslive-cli.fsproj", "{47CDCE8F-BF6F-4FFB-8606-FC7835C68350}"
25 | EndProject
26 | Global
27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
28 | Debug|Any CPU = Debug|Any CPU
29 | Debug|x64 = Debug|x64
30 | Debug|x86 = Debug|x86
31 | Release|Any CPU = Release|Any CPU
32 | Release|x64 = Release|x64
33 | Release|x86 = Release|x86
34 | EndGlobalSection
35 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
36 | {14A80081-D97A-405D-9A49-C686ABF00842}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {14A80081-D97A-405D-9A49-C686ABF00842}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {14A80081-D97A-405D-9A49-C686ABF00842}.Debug|x64.ActiveCfg = Debug|Any CPU
39 | {14A80081-D97A-405D-9A49-C686ABF00842}.Debug|x64.Build.0 = Debug|Any CPU
40 | {14A80081-D97A-405D-9A49-C686ABF00842}.Debug|x86.ActiveCfg = Debug|Any CPU
41 | {14A80081-D97A-405D-9A49-C686ABF00842}.Debug|x86.Build.0 = Debug|Any CPU
42 | {14A80081-D97A-405D-9A49-C686ABF00842}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {14A80081-D97A-405D-9A49-C686ABF00842}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {14A80081-D97A-405D-9A49-C686ABF00842}.Release|x64.ActiveCfg = Release|Any CPU
45 | {14A80081-D97A-405D-9A49-C686ABF00842}.Release|x64.Build.0 = Release|Any CPU
46 | {14A80081-D97A-405D-9A49-C686ABF00842}.Release|x86.ActiveCfg = Release|Any CPU
47 | {14A80081-D97A-405D-9A49-C686ABF00842}.Release|x86.Build.0 = Release|Any CPU
48 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Debug|x64.ActiveCfg = Debug|Any CPU
51 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Debug|x64.Build.0 = Debug|Any CPU
52 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Debug|x86.ActiveCfg = Debug|Any CPU
53 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Debug|x86.Build.0 = Debug|Any CPU
54 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Release|Any CPU.ActiveCfg = Release|Any CPU
55 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Release|Any CPU.Build.0 = Release|Any CPU
56 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Release|x64.ActiveCfg = Release|Any CPU
57 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Release|x64.Build.0 = Release|Any CPU
58 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Release|x86.ActiveCfg = Release|Any CPU
59 | {BB0051D2-8692-449E-B20F-6DBD605C7553}.Release|x86.Build.0 = Release|Any CPU
60 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
61 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
62 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Debug|x64.ActiveCfg = Debug|Any CPU
63 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Debug|x64.Build.0 = Debug|Any CPU
64 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Debug|x86.ActiveCfg = Debug|Any CPU
65 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Debug|x86.Build.0 = Debug|Any CPU
66 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
67 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Release|Any CPU.Build.0 = Release|Any CPU
68 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Release|x64.ActiveCfg = Release|Any CPU
69 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Release|x64.Build.0 = Release|Any CPU
70 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Release|x86.ActiveCfg = Release|Any CPU
71 | {1661E77C-1DD4-4920-9345-C86D04B37DD6}.Release|x86.Build.0 = Release|Any CPU
72 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
73 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
74 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Debug|x64.ActiveCfg = Debug|Any CPU
75 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Debug|x64.Build.0 = Debug|Any CPU
76 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Debug|x86.ActiveCfg = Debug|Any CPU
77 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Debug|x86.Build.0 = Debug|Any CPU
78 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
79 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Release|Any CPU.Build.0 = Release|Any CPU
80 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Release|x64.ActiveCfg = Release|Any CPU
81 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Release|x64.Build.0 = Release|Any CPU
82 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Release|x86.ActiveCfg = Release|Any CPU
83 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5}.Release|x86.Build.0 = Release|Any CPU
84 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
85 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
86 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Debug|x64.ActiveCfg = Debug|Any CPU
87 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Debug|x64.Build.0 = Debug|Any CPU
88 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Debug|x86.ActiveCfg = Debug|Any CPU
89 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Debug|x86.Build.0 = Debug|Any CPU
90 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
91 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Release|Any CPU.Build.0 = Release|Any CPU
92 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Release|x64.ActiveCfg = Release|Any CPU
93 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Release|x64.Build.0 = Release|Any CPU
94 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Release|x86.ActiveCfg = Release|Any CPU
95 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C}.Release|x86.Build.0 = Release|Any CPU
96 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
97 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Debug|Any CPU.Build.0 = Debug|Any CPU
98 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Debug|x64.ActiveCfg = Debug|Any CPU
99 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Debug|x64.Build.0 = Debug|Any CPU
100 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Debug|x86.ActiveCfg = Debug|Any CPU
101 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Debug|x86.Build.0 = Debug|Any CPU
102 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Release|Any CPU.ActiveCfg = Release|Any CPU
103 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Release|Any CPU.Build.0 = Release|Any CPU
104 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Release|x64.ActiveCfg = Release|Any CPU
105 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Release|x64.Build.0 = Release|Any CPU
106 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Release|x86.ActiveCfg = Release|Any CPU
107 | {0DDF9662-A5E5-41B2-ADF2-D38447312611}.Release|x86.Build.0 = Release|Any CPU
108 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
109 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Debug|Any CPU.Build.0 = Debug|Any CPU
110 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Debug|x64.ActiveCfg = Debug|Any CPU
111 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Debug|x64.Build.0 = Debug|Any CPU
112 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Debug|x86.ActiveCfg = Debug|Any CPU
113 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Debug|x86.Build.0 = Debug|Any CPU
114 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Release|Any CPU.ActiveCfg = Release|Any CPU
115 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Release|Any CPU.Build.0 = Release|Any CPU
116 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Release|x64.ActiveCfg = Release|Any CPU
117 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Release|x64.Build.0 = Release|Any CPU
118 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Release|x86.ActiveCfg = Release|Any CPU
119 | {EC522CB8-5E90-4F4E-9059-62DAB763A544}.Release|x86.Build.0 = Release|Any CPU
120 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
121 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Debug|Any CPU.Build.0 = Debug|Any CPU
122 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Debug|x64.ActiveCfg = Debug|Any CPU
123 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Debug|x64.Build.0 = Debug|Any CPU
124 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Debug|x86.ActiveCfg = Debug|Any CPU
125 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Debug|x86.Build.0 = Debug|Any CPU
126 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Release|Any CPU.ActiveCfg = Release|Any CPU
127 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Release|Any CPU.Build.0 = Release|Any CPU
128 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Release|x64.ActiveCfg = Release|Any CPU
129 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Release|x64.Build.0 = Release|Any CPU
130 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Release|x86.ActiveCfg = Release|Any CPU
131 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350}.Release|x86.Build.0 = Release|Any CPU
132 | EndGlobalSection
133 | GlobalSection(SolutionProperties) = preSolution
134 | HideSolutionNode = FALSE
135 | EndGlobalSection
136 | GlobalSection(NestedProjects) = preSolution
137 | {14A80081-D97A-405D-9A49-C686ABF00842} = {43FBDDAF-4E0A-4D82-BCB2-002472E7F170}
138 | {BB0051D2-8692-449E-B20F-6DBD605C7553} = {3D12606D-9919-4874-A10C-E37F1CB84CBC}
139 | {1661E77C-1DD4-4920-9345-C86D04B37DD6} = {3D12606D-9919-4874-A10C-E37F1CB84CBC}
140 | {70B2741D-D9AC-47B2-9D0C-78478562C6D5} = {3D12606D-9919-4874-A10C-E37F1CB84CBC}
141 | {A9F69171-7544-4029-A2EA-4E6FF49D2E1C} = {43FBDDAF-4E0A-4D82-BCB2-002472E7F170}
142 | {0DDF9662-A5E5-41B2-ADF2-D38447312611} = {3D12606D-9919-4874-A10C-E37F1CB84CBC}
143 | {EC522CB8-5E90-4F4E-9059-62DAB763A544} = {3D12606D-9919-4874-A10C-E37F1CB84CBC}
144 | {47CDCE8F-BF6F-4FFB-8606-FC7835C68350} = {3D12606D-9919-4874-A10C-E37F1CB84CBC}
145 | EndGlobalSection
146 | GlobalSection(ExtensibilityGlobals) = postSolution
147 | SolutionGuid = {4BFB89E1-195E-46FB-8AB6-7632542E82D5}
148 | EndGlobalSection
149 | EndGlobal
150 |
--------------------------------------------------------------------------------