Skip to content

Commit ff20f70

Browse files
authored
cross-platform wrapper around memory-backed memorymappedfile creation (#8403)
* cross-platform wrapper around memory-backed memorymappedfile creation * reenable FCS tests on non-windows platforms * publish test results on non-windows platforms * ensure correct .net sdk versions are installed regardless of VM image preloads * simple workaround for missing mscorlib versions to TFMs * publish per-framework xml results and report them in AZDO * rework mono fixes to use ByteArrayMemory directly * move runningOnMono call to bytes.fs and patch up callsites * revert to build-only on mono-containing platforms another MR will be required to fix the underlying logic before those can be changed to run tests again * trigger ci
1 parent e71fd03 commit ff20f70

22 files changed

+217
-118
lines changed

azure-pipelines.yml

+46-12
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,11 @@ stages:
342342
steps:
343343
- checkout: self
344344
clean: true
345+
- task: UseDotNet@2
346+
displayName: 'Use .NET Core sdk'
347+
inputs:
348+
useGlobalJson: true
349+
workingDirectory: fcs
345350
- script: fcs\build.cmd TestAndNuget
346351
displayName: Build / Test
347352
- task: PublishTestResults@2
@@ -350,6 +355,7 @@ stages:
350355
testResultsFormat: 'NUnit'
351356
testResultsFiles: '*.xml'
352357
searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/Release'
358+
publishRunAttachments: true
353359
continueOnError: true
354360
condition: always()
355361

@@ -362,20 +368,48 @@ stages:
362368
steps:
363369
- checkout: self
364370
clean: true
371+
- task: UseDotNet@2
372+
displayName: 'Use .NET Core sdk'
373+
inputs:
374+
useGlobalJson: true
375+
workingDirectory: fcs
365376
- script: ./fcs/build.sh
366-
displayName: Build
377+
displayName: Build / Test
378+
- task: PublishTestResults@2
379+
displayName: Publish Test Results
380+
inputs:
381+
testResultsFormat: 'NUnit'
382+
testResultsFiles: '*.xml'
383+
searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/Release'
384+
publishRunAttachments: true
385+
continueOnError: true
386+
condition: always()
367387

368-
# - job: MacOS_FCS
369-
# pool:
370-
# vmImage: macOS-latest
371-
# variables:
372-
# - name: _SignType
373-
# value: Test
374-
# steps:
375-
# - checkout: self
376-
# clean: true
377-
# - script: ./fcs/build.sh
378-
# displayName: Build
388+
- job: MacOS_FCS
389+
pool:
390+
vmImage: macOS-latest
391+
variables:
392+
- name: _SignType
393+
value: Test
394+
steps:
395+
- checkout: self
396+
clean: true
397+
- task: UseDotNet@2
398+
displayName: 'Use .NET Core sdk'
399+
inputs:
400+
useGlobalJson: true
401+
workingDirectory: fcs
402+
- script: ./fcs/build.sh
403+
displayName: Build / Test
404+
- task: PublishTestResults@2
405+
displayName: Publish Test Results
406+
inputs:
407+
testResultsFormat: 'NUnit'
408+
testResultsFiles: '*.xml'
409+
searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/Release'
410+
publishRunAttachments: true
411+
continueOnError: true
412+
condition: always()
379413

380414
#---------------------------------------------------------------------------------------------------------------------#
381415
# Post Build #

eng/Versions.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@
183183
<NUnitVersion>3.11.0</NUnitVersion>
184184
<NUnit3TestAdapterVersion>3.11.2</NUnit3TestAdapterVersion>
185185
<NUnitLiteVersion>3.11.0</NUnitLiteVersion>
186-
<NunitXmlTestLoggerVersion>2.1.36</NunitXmlTestLoggerVersion>
186+
<NunitXmlTestLoggerVersion>2.1.41</NunitXmlTestLoggerVersion>
187187
<RoslynToolsSignToolVersion>1.0.0-beta2-dev3</RoslynToolsSignToolVersion>
188188
<StrawberryPerlVersion>5.28.0.1</StrawberryPerlVersion>
189189
<StreamJsonRpcVersion>2.0.187</StreamJsonRpcVersion>

fcs/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<Import Project="..\netfx.props" />
3+
<Import Project="..\..\eng\Versions.props"/> <!-- keep our test deps in line with the overall compiler -->
34
<PropertyGroup>
45
<TargetFrameworks>$(FcsTargetNetFxFramework);netcoreapp3.0</TargetFrameworks>
56
<DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference>
@@ -60,6 +61,9 @@
6061
<Compile Include="$(FSharpSourcesRoot)\..\tests\service\TreeVisitorTests.fs">
6162
<Link>TreeVisitorTests.fs</Link>
6263
</Compile>
64+
<Compile Include="$(FSharpSourcesRoot)\..\tests\service\ScriptOptionsTests.fs">
65+
<Link>ScriptOptionsTests.fs</Link>
66+
</Compile>
6367
<Compile Include="$(FSharpSourcesRoot)\..\tests\service\Program.fs" Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
6468
<Link>Program.fs</Link>
6569
</Compile>
@@ -71,10 +75,10 @@
7175
<ItemGroup>
7276
<PackageReference Include="FSharp.Core" Version="$(FcsFSharpCorePkgVersion)" />
7377
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.0" />
74-
<PackageReference Include="NUnit" Version="3.9.0" />
75-
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
76-
<PackageReference Include="NunitXml.TestLogger" Version="2.1.36" />
7778
<PackageReference Include="Dotnet.ProjInfo" Version="0.20.0" />
79+
<PackageReference Include="NUnit" Version="$(NUnitVersion)" />
80+
<PackageReference Include="NUnit3TestAdapter" Version="$(NUnit3TestAdapterVersion)" />
81+
<PackageReference Include="NunitXml.TestLogger" Version="$(NunitXmlTestLoggerVersion)" />
7882
<ProjectReference Include="..\FSharp.Compiler.Service\FSharp.Compiler.Service.fsproj" />
7983
</ItemGroup>
8084
<ItemGroup Condition="'$(TargetFramework)' == '$(FcsTargetNetFxFramework)'">

fcs/build.fsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ Target.create "Test" (fun _ ->
7373
// This project file is used for the netcoreapp2.0 tests to work out reference sets
7474
runDotnet __SOURCE_DIRECTORY__ "build" "../tests/projects/Sample_NETCoreSDK_FSharp_Library_netstandard2_0/Sample_NETCoreSDK_FSharp_Library_netstandard2_0.fsproj -nodereuse:false -v n /restore /p:DisableCompilerRedirection=true"
7575

76-
// Now run the tests
77-
let logFilePath = Path.Combine(__SOURCE_DIRECTORY__, "..", "artifacts", "TestResults", "Release", "FSharp.Compiler.Service.Test.xml")
78-
runDotnet __SOURCE_DIRECTORY__ "test" (sprintf "FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj --no-restore --no-build -nodereuse:false -v n -c Release --test-adapter-path . --logger \"nunit;LogFilePath=%s\"" logFilePath)
76+
// Now run the tests (different output files per TFM)
77+
let logFilePath = Path.Combine(__SOURCE_DIRECTORY__, "..", "artifacts", "TestResults", "Release", "FSharp.Compiler.Service.Test.{framework}.xml")
78+
runDotnet __SOURCE_DIRECTORY__ "test" (sprintf "FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj --no-restore --no-build -nodereuse:false -v n -c Release --logger \"nunit;LogFilePath=%s\"" logFilePath)
7979
)
8080

8181
Target.create "NuGet" (fun _ ->

fcs/samples/FscExe/FscMain.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ open System.IO
88
open System.Reflection
99
open System.Runtime.CompilerServices
1010
open FSharp.Compiler.SourceCodeServices
11-
open FSharp.Compiler.AbstractIL.IL // runningOnMono
11+
open FSharp.Compiler.AbstractIL.Internal.Utils // runningOnMono
1212
open FSharp.Compiler.AbstractIL.Internal.Library
1313
open FSharp.Compiler.ErrorLogger
1414

src/absil/bytes.fs

+77-51
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,25 @@ open FSharp.NativeInterop
1212

1313
#nowarn "9"
1414

15+
module Utils =
16+
let runningOnMono =
17+
#if ENABLE_MONO_SUPPORT
18+
// Officially supported way to detect if we are running on Mono.
19+
// See http://www.mono-project.com/FAQ:_Technical
20+
// "How can I detect if am running in Mono?" section
21+
try
22+
System.Type.GetType ("Mono.Runtime") <> null
23+
with _ ->
24+
// Must be robust in the case that someone else has installed a handler into System.AppDomain.OnTypeResolveEvent
25+
// that is not reliable.
26+
// This is related to bug 5506--the issue is actually a bug in VSTypeResolutionService.EnsurePopulated which is
27+
// called by OnTypeResolveEvent. The function throws a NullReferenceException. I'm working with that team to get
28+
// their issue fixed but we need to be robust here anyway.
29+
false
30+
#else
31+
false
32+
#endif
33+
1534
module internal Bytes =
1635
let b0 n = (n &&& 0xFF)
1736
let b1 n = ((n >>> 8) &&& 0xFF)
@@ -296,65 +315,72 @@ type ByteMemory with
296315
member x.AsReadOnly() = ReadOnlyByteMemory x
297316

298317
static member CreateMemoryMappedFile(bytes: ReadOnlyByteMemory) =
299-
let length = int64 bytes.Length
300-
let mmf =
318+
if Utils.runningOnMono
319+
then
320+
// mono's MemoryMappedFile implementation throws with null `mapName`, so we use byte arrays instead: https://github.com/mono/mono/issues/10245
321+
ByteArrayMemory.FromArray (bytes.ToArray()) :> ByteMemory
322+
else
323+
let length = int64 bytes.Length
301324
let mmf =
302-
MemoryMappedFile.CreateNew(
303-
null,
304-
length,
305-
MemoryMappedFileAccess.ReadWrite,
306-
MemoryMappedFileOptions.None,
307-
HandleInheritability.None)
308-
use stream = mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.ReadWrite)
309-
bytes.CopyTo stream
310-
mmf
311-
312-
let accessor = mmf.CreateViewAccessor(0L, length, MemoryMappedFileAccess.ReadWrite)
313-
RawByteMemory.FromUnsafePointer(accessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), int length, (mmf, accessor))
325+
let mmf = MemoryMappedFile.CreateNew(null, length, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, HandleInheritability.None)
326+
use stream = mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.ReadWrite)
327+
bytes.CopyTo stream
328+
mmf
329+
330+
let accessor = mmf.CreateViewAccessor(0L, length, MemoryMappedFileAccess.ReadWrite)
331+
RawByteMemory.FromUnsafePointer(accessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), int length, (mmf, accessor))
314332

315333
static member FromFile(path, access, ?canShadowCopy: bool) =
316334
let canShadowCopy = defaultArg canShadowCopy false
317335

318-
let memoryMappedFileAccess =
319-
match access with
320-
| FileAccess.Read -> MemoryMappedFileAccess.Read
321-
| FileAccess.Write -> MemoryMappedFileAccess.Write
322-
| _ -> MemoryMappedFileAccess.ReadWrite
336+
if Utils.runningOnMono
337+
then
338+
// mono's MemoryMappedFile implementation throws with null `mapName`, so we use byte arrays instead: https://github.com/mono/mono/issues/10245
339+
let bytes = File.ReadAllBytes path
340+
ByteArrayMemory.FromArray bytes
341+
else
342+
let memoryMappedFileAccess =
343+
match access with
344+
| FileAccess.Read -> MemoryMappedFileAccess.Read
345+
| FileAccess.Write -> MemoryMappedFileAccess.Write
346+
| _ -> MemoryMappedFileAccess.ReadWrite
323347

324-
let mmf, accessor, length =
325348
let fileStream = File.Open(path, FileMode.Open, access, FileShare.Read)
349+
326350
let length = fileStream.Length
327-
let mmf =
328-
if canShadowCopy then
329-
let mmf =
330-
MemoryMappedFile.CreateNew(
331-
null,
332-
length,
333-
MemoryMappedFileAccess.ReadWrite,
334-
MemoryMappedFileOptions.None,
335-
HandleInheritability.None)
336-
use stream = mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.ReadWrite)
337-
fileStream.CopyTo(stream)
338-
fileStream.Dispose()
339-
mmf
340-
else
341-
MemoryMappedFile.CreateFromFile(
342-
fileStream,
343-
null,
344-
length,
345-
memoryMappedFileAccess,
346-
HandleInheritability.None,
347-
leaveOpen=false)
348-
mmf, mmf.CreateViewAccessor(0L, length, memoryMappedFileAccess), length
349-
350-
// Validate MMF with the access that was intended.
351-
match access with
352-
| FileAccess.Read when not accessor.CanRead -> invalidOp "Cannot read file"
353-
| FileAccess.Write when not accessor.CanWrite -> invalidOp "Cannot write file"
354-
| FileAccess.ReadWrite when not accessor.CanRead || not accessor.CanWrite -> invalidOp "Cannot read or write file"
355-
| _ -> ()
356-
357-
RawByteMemory.FromUnsafePointer(accessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), int length, (mmf, accessor))
351+
352+
let mmf, accessor, length =
353+
let mmf =
354+
if canShadowCopy then
355+
let mmf =
356+
MemoryMappedFile.CreateNew(
357+
null,
358+
length,
359+
MemoryMappedFileAccess.ReadWrite,
360+
MemoryMappedFileOptions.None,
361+
HandleInheritability.None)
362+
use stream = mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.ReadWrite)
363+
fileStream.CopyTo(stream)
364+
fileStream.Dispose()
365+
mmf
366+
else
367+
MemoryMappedFile.CreateFromFile(
368+
fileStream,
369+
null,
370+
length,
371+
memoryMappedFileAccess,
372+
HandleInheritability.None,
373+
leaveOpen=false)
374+
mmf, mmf.CreateViewAccessor(0L, length, memoryMappedFileAccess), length
375+
376+
// Validate MMF with the access that was intended.
377+
match access with
378+
| FileAccess.Read when not accessor.CanRead -> invalidOp "Cannot read file"
379+
| FileAccess.Write when not accessor.CanWrite -> invalidOp "Cannot write file"
380+
| FileAccess.ReadWrite when not accessor.CanRead || not accessor.CanWrite -> invalidOp "Cannot read or write file"
381+
| _ -> ()
382+
383+
RawByteMemory.FromUnsafePointer(accessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), int length, (mmf, accessor))
358384

359385
static member FromUnsafePointer(addr, length, holder: obj) =
360386
RawByteMemory(NativePtr.ofNativeInt addr, length, holder) :> ByteMemory

src/absil/bytes.fsi

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ namespace FSharp.Compiler.AbstractIL.Internal
55

66
open System.IO
77
open Internal.Utilities
8-
98
open FSharp.Compiler.AbstractIL
109
open FSharp.Compiler.AbstractIL.Internal
1110

11+
module Utils =
12+
val runningOnMono: bool
1213

1314
module internal Bytes =
1415
/// returned int will be 0 <= x <= 255

src/absil/il.fs

-18
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,6 @@ open Internal.Utilities
2525

2626
let logging = false
2727

28-
let runningOnMono =
29-
#if ENABLE_MONO_SUPPORT
30-
// Officially supported way to detect if we are running on Mono.
31-
// See http://www.mono-project.com/FAQ:_Technical
32-
// "How can I detect if am running in Mono?" section
33-
try
34-
System.Type.GetType ("Mono.Runtime") <> null
35-
with e->
36-
// Must be robust in the case that someone else has installed a handler into System.AppDomain.OnTypeResolveEvent
37-
// that is not reliable.
38-
// This is related to bug 5506--the issue is actually a bug in VSTypeResolutionService.EnsurePopulated which is
39-
// called by OnTypeResolveEvent. The function throws a NullReferenceException. I'm working with that team to get
40-
// their issue fixed but we need to be robust here anyway.
41-
false
42-
#else
43-
false
44-
#endif
45-
4628
let _ = if logging then dprintn "* warning: Il.logging is on"
4729

4830
let int_order = LanguagePrimitives.FastGenericComparer<int>

src/absil/il.fsi

-2
Original file line numberDiff line numberDiff line change
@@ -2012,8 +2012,6 @@ type ILPropertyRef =
20122012
member Name: string
20132013
interface System.IComparable
20142014

2015-
val runningOnMono: bool
2016-
20172015
type ILReferences =
20182016
{ AssemblyReferences: ILAssemblyRef list
20192017
ModuleReferences: ILModuleRef list }

src/absil/ilread.fs

+4-3
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ open System.Text
2020
open Internal.Utilities
2121
open Internal.Utilities.Collections
2222
open FSharp.NativeInterop
23-
open FSharp.Compiler.AbstractIL.Internal
24-
open FSharp.Compiler.AbstractIL.Internal.Support
2523
open FSharp.Compiler.AbstractIL.Diagnostics
24+
open FSharp.Compiler.AbstractIL.IL
25+
open FSharp.Compiler.AbstractIL.Internal
2626
open FSharp.Compiler.AbstractIL.Internal.BinaryConstants
27-
open FSharp.Compiler.AbstractIL.IL
2827
open FSharp.Compiler.AbstractIL.Internal.Library
28+
open FSharp.Compiler.AbstractIL.Internal.Support
29+
open FSharp.Compiler.AbstractIL.Internal.Utils
2930
open FSharp.Compiler.ErrorLogger
3031
open FSharp.Compiler.Range
3132
open System.Reflection

src/absil/ilreflect.fs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ open System.Collections.Generic
1717
open FSharp.Compiler.AbstractIL
1818
open FSharp.Compiler.AbstractIL.Internal
1919
open FSharp.Compiler.AbstractIL.Internal.Library
20+
open FSharp.Compiler.AbstractIL.Internal.Utils
2021
open FSharp.Compiler.AbstractIL.Diagnostics
2122
open FSharp.Compiler.AbstractIL.IL
2223
open FSharp.Compiler.ErrorLogger

0 commit comments

Comments
 (0)