Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VS editor shows bogus errors when scripts use multi-hop #r and #load with relative paths #273

Closed
smoothdeveloper opened this issue Feb 26, 2015 · 18 comments
Labels

Comments

@smoothdeveloper
Copy link
Contributor

// ./bar/baz/BarBaz.fsx
#r "../../lib/zoo.dll"
// ./foo/Foo.fsx
#load "../Bar/Baz/BarBaz.fsx"
// says: Assembly Reference '../../lib/....' was not found or is invalid

This should work, this limit scripting to having everything in the same directory, or other convoluted restriction (everything at same nesting level) which is not manageable.

I believe when a relative path is used in a script or source file, in the context of #r or #load fsc or fsi should never assume it's current directory, nor the top level file invoked nor anything like this, it should resolve from the path of the file containing the reference.

While I'd like the focus to be on what I describe above, I want to mention two things related to path resolution:

  1. See this quirk impacting a type provider, depending from where you invoke the compiler:

fsprojects/FSharp.Data.SqlClient#112 (comment)

I believe type provider should have a way to get location of file being compiled (even if it applies only when compiling from files), this would give the implementor a way to solve the strange behaviour we see depending where compiler is invoked.

  1. In a solution containing many projects (vb.net, c#, f#) located at various places (not in a single folder, not at the same nesting level), I often get those messages while building in VS by building a project say at nesting level 1 (relative to .sln) which depends on a f# project at nesting level 3:
2>  C:\Program Files (x86)\Microsoft SDKs\F#\3.1\Framework\v4.0\fsc.exe -o:obj\Debug\******.dll -g --debug:full --noframework --define:DEBUG --define:TRACE --optimize- --tailcalls- -r:C:\workingcopies\gittrunkclone\******.dll -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.3.0.0\FSharp.Core.dll" -r:C:\workingcopies\gittrunkclone\tools\nuget\packages\FSharp.Data.SqlClient\lib\net40\FSharp.Data.SqlClient.dll -r:C:\Lib\lcg.Shared\lcg.Common.FrameworkExtensions.dll -r:C:\thirdpartydependencies\log4net\bin\cli\log4net.dll -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll" -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Core.dll" -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Data.dll" -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll" -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Numerics.dll" -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Windows.Forms.dll" -r:C:\workingcopies\gittrunkclone\******.dll --target:library --warn:3 --warnaserror:76 --vserrors --validate-type-providers --LCID:1033 --utf8output --fullpaths --flaterrors --highentropyva- --sqmsessionguid:4d2dfc37-e490-446b-b0c8-c00ad4833128 --keyfile:"../../../../Tools/******.snk" "C:\Users\******\AppData\Local\Temp\.NETFramework,Version=v4.0.AssemblyAttributes.fs" AssemblyInfo.fs \******.fs 
2>FSC: error FS0225: Source file 'C:\workingcopies\gittrunkclone\******\AssemblyInfo.fs' could not be found
2>FSC: error FS0225: Source file 'C:\workingcopies\gittrunkclone\******.fs' could not be found
2>FSC: error FS0225: Source file 'C:\workingcopies\gittrunkclone\******.fs' could not be found
2>FSC: error FS0225: Source file 'C:\workingcopies\gittrunkclone\******.fs' could not be found
2>FSC: error FS0225: Source file 'C:\workingcopies\gittrunkclone\******.fs' could not be found
2>FSC: error FS0225: Source file 'C:\workingcopies\gittrunkclone\******.fs' could not be found
2>Done building project "******.fsproj" -- FAILED.

I never get those issues with vb.net or c# project files, this somehow doesn't fail the build but clutters the output pane with misleading information.

@dsyme
Copy link
Contributor

dsyme commented Feb 26, 2015

Have you tried adding

#I __SOURCE_DIRECTORY__

to your script?

@smoothdeveloper
Copy link
Contributor Author

@dsyme thanks for the hint which helps.

Are you saying it's a workaround or it's the expected way to do it?

I believe while it might allow me to work around the issue with #r assembly loading, I believe it might lead to other issues if it also affects #load resolution in case of having a script with same file name at different places.

I haven't figured a way to encounter error related to #load once the assembly reference issue is cleared out.

Ideally, I would like to try to gather information on the matter, the documentation on msdn is not very detailled:

https://msdn.microsoft.com/en-us/library/dd233175.aspx

#load Reads a source file, compiles it, and runs it.
#r References an assembly.

As there is no details explaining how the location resolution works for both, I would expect the behaviour to be identical; apparently #load works one way (https://github.com/Microsoft/visualfsharp/blob/fac44095a8dbc7303efa9866e0e2d9dddbdeae5d/tests/fsharpqa/Source/InteractiveSession/Misc/ScriptTest/LoadScriptResolution01.fsx a bug was fixed) and #r another.

It would be good to explain how those differs or to make them behave identically.

I would ideally prefer the least expected surprise with paths always relative to the file which contain the declaration, never from where the compiler/interpreter is invoked or the file including the other is located.

@dsyme
Copy link
Contributor

dsyme commented Feb 26, 2015

Yes, I agree this is inconsistent. Using #I __SOURCE_DIRECTORY__ is the best workaround I believe. I don't believe it's widely known as I see lots of other crazy workarounds

It's hard to make a fix to this since it would definitely be a breaking change.

@latkin - you might like to take a look at the difference in behaviour here. Perhaps an MSDN doc fix would help here too?

@KevinRansom
Copy link
Member

Just throwing this out there, and I have given it exactly zero thought so far but, maybe we can add a new directive: #location "blah" or # workingdir "blah" or #projectdir "blah" or something similar and when that is specified relative paths in #load and #r are relative to that. That wouldn't be a breaking change. And it may nail down and make deterministic those relative paths.

@smoothdeveloper
Copy link
Contributor Author

@dsyme isn't this a breaking changes leading to compile error (script won't run at all) as opposed to those happening silently at runtime in case of resolution failure?

As far as I understand the scripts relying on #I __SOURCE_DIRECTORY__ will keep working even after fixing the inconsistency.

Also, the bug fix / test case referenced (LoadScriptResolution01.fsx ) was most probably a breaking change.

I understand being very cautious about breaking changes in general (API, libraries) but this seems to be a case where assembly references issues will popup when something really exotic was done in order to work around the issue we are discussing.

I might of course overlook other cases in the wild.

@KevinRansom what you propose would also work,but would add confusion to a system I assume should be very simple / surprise free (from user perspective, not saying it's easy to implement correctly).

If we really want to go this route, allowing __SOURCE_DIRECTORY__ to be used in #r references might be a way to solve the issue without adding directives.

Similarly in the appendix regarding TP, it would make sense to expose a way to obtain the file location of code instanciating the TP, right now the library tries to do it's job based on the place of invocation of the compiler, on which we don't have much control, that works well for flat solutions with all files in a single folder but beyond that it's painful.

My vote goes for fixing inconsistent behaviour (make it work like #load which I tend to belive does what is expected all the time).

Thanks a lot for the workaround and considering the issue.

@latkin
Copy link
Contributor

latkin commented Feb 27, 2015

Did anyone else try this? I do not repro -- FSI behavior is just as requested: #r relative path in #loaded file is relative to that file, not to the one that loaded it.

See my session here, showing content and location of all files. Do I have the scenario wrong?

image

The implicitIncludeDir config is updated as FSI processes each input file, so resolution are always relative to the currently-processed file. See here and here.

@smoothdeveloper
Copy link
Contributor Author

I have a repro case, and the work around actually doesn't help 😞 (it's the case I initially hit but I simplified too much) :

You only need two files:

./fub/baz/boo/fubbazboo.fsx

#load "../../../bar/baz/barbaz.fsx"
printfn "%O" BarBaz.barbaz
printfn "%O" typeof<BarBaz.wmi>

./bar/baz/barbaz.fsx

//#I __SOURCE_DIRECTORY__ // you can also uncomment it
#r "System.Management"
#r "../../lib/FSharp.Management.dll"
#r "../../lib/FSharp.Management.WMI.dll"
let barbaz = 2
type wmi = FSharp.Management.WmiProvider<"localhost">

./lib/ (I put FSharp.Management there)

scripting load and r resolution issues 2

scripting load and r resolution issues

running ./fub/baz/boo/fubbazboo.fsx from various places with fsi lead to

C:\tmp>"C:\Program Files (x86)\Microsoft SDKs\F#\4.0\Framework\v4.0\Fsi.exe" c:\tmp\fub\baz\boo\fubbazboo.fsx


fubbazboo.fsx(3,14): error FS0039: The namespace or module 'BarBaz' is not defined

C:\tmp>

Hope this helps.

@dsyme
Copy link
Contributor

dsyme commented Feb 27, 2015

Yes, IIRC problems come when you #load a file in another directory that itself contains relative #load, or if you run a script with relative #load from a different location using the command line fsi.exe

@rojepp
Copy link
Contributor

rojepp commented Feb 27, 2015

The #I ___SOURCE_DIRECTORY workaround will not do anything for type providers that read files at compile-time, or am I misunderstanding?

@smoothdeveloper
Copy link
Contributor Author

@rojepp: I think #I is only usable from script files, so no won't help. I just mentionned this because I think all path resolution concerns should be addressed with an as global as possible picture, so the behaviour can be consistent across the landscape.

@dsyme, @KevinRansom, @latkin:

  • do we need a repro (can you repro with what I gave?), I got things working by having a ./fub/baz/boo/fubbazboo.fsx including ./foo/foo.fsx including ./bar/baz/barbaz.fsx but I would not say it qualify as a work around, it's even more confusing IMHO that it could work this way
  • do we agree that a consistent and predictible script and assembly resolution (always from script file location, and eventually adding #I for assembly reference to the mix when fail to get a match) is strongly needed for the F# as scripting language story?

my understanding is such:

#load

  • this one is simple, it's always relative to the file using the directive (unless of course absolute path is given)

#I

  • should apply only to assembly references;
  • putting it in scripts is useful to shorten assembly references (point to a specific folder containing assemblies)
  • it's identical to a fsc/fsi include switch
  • it should have no effect on how #load imports another F# source file
  • to be evaluate for each #I in order they were given, starting from those given to fsc/fsi, then order they appear in loaded scripts, within a script, if path is relative, you have guessed that it's similar to combining it with SOURCE_DIRECTORY of the script having this directive.

#r

  • default case (no #I nor fsc/fsi include switch) is lookup relative from script file, even if script is loaded from another script
  • if #I (or fsc/fsi include switch) are specified AND a relative assembly reference is not resolved, then they kick in, relative assebmly is crawled upward the directory structure and concatenated to the #I folder, if a match is found, resolved, if not found crawl until only assembly name remains and is eventually found in included folders, if not then resolution of assembly fails

crawling behaviour

without incudes

#r "../../../../some/path/to/myassembly.dll"
  • ../../../some/path/to/myassembly.dll" not found
  • ../../some/path/to/myassembly.dll" not found
  • ../some/path/to/myassembly.dll" not found
  • myassembly.dll" not found

with incudes and subfolder containing assemblies

if #I has a folder containing some/path/to/myassembly.dll:

#I "c:/myassemblies"
#r "../../../../some/path/to/myassembly.dll"
  • ../../../some/path/to/myassembly.dll" not found
  • ../../some/path/to/myassembly.dll" not found
  • ../some/path/to/myassembly.dll" not found
  • myassembly.dll" not found
  • c:/myassemblies/../../../some/path/to/myassembly.dll" not found
  • c:/myassemblies/../../some/path/to/myassembly.dll" not found
  • c:/myassemblies/../some/path/to/myassembly.dll" not found
  • c:/myassemblies/some/path/to/myassembly.dll" found

with incudes of a folder containing assemblies

if #I has myassembly.dll

#I "c:/myassemblies"
#r "../../../../some/path/to/myassembly.dll"
  • ../../../some/path/to/myassembly.dll" not found
  • ../../some/path/to/myassembly.dll" not found
  • ../some/path/to/myassembly.dll" not found
  • myassembly.dll" not found
  • c:/myassemblies/../../../some/path/to/myassembly.dll" not found
  • c:/myassemblies/../../some/path/to/myassembly.dll" not found
  • c:/myassemblies/../some/path/to/myassembly.dll" not found
  • c:/myassemblies/some/path/to/myassembly.dll" not found
  • c:/myassemblies/myassembly.dll" found

Please let me know if those ''ideal assumptions'' are correct, and whether you find out they work correctly or not in current implementations of F# (I initially hit this issue with 3.1, but reproduced with bits shipping in VS2015).

I hope I gave enough details explaining how I assume it should work 😃

@latkin
Copy link
Contributor

latkin commented Feb 27, 2015

I still do not repro any issue with crawling behavior in FSI.

image

I'm pretty sure your issue is unrelated to relative paths or crawling, and rather due to the fact that the casing of the #load directive affects the generated module name for that file. The generated module name uses the same casing as the file name in the #load statement, but with the first letter capitalized.

So if you do #load "../../../bar/baz/barbaz.fsx" like in your repro, the generated module is Barbaz, not BarBaz. If one does #load "../../../bar/baz/bArBaZ.fsx" the module is now BArBaZ. It doesn't matter what the actual file name is, or whether the load is from relative path, absolute path, or #I.

Can you check if this fixes your problem?

@smoothdeveloper
Copy link
Contributor Author

@latkin, man, I can't believe I couldn't figure this out, issue with case sensitiveness on non case sensitive file system...

So what remains is a bug in VS tooling:

  • from command line, it works with and without #I __SOURCE_DIRECTORY__ in /bar/baz/BarBaz.fsx
  • in VS, you'll get the squiggles and lost intellisense if #I __SOURCE_DIRECTORY__ is not there

Both in VS and fsc/fsi, many ways to shoot self in the foot with:

  • case sensitiveness disregarding of case sensitivity of file system
  • lack of diagnostic from compiler / tool to help user figure out a reference which could fail depending file system / how reference is written in script

my guess is that tools should take physical name of resolved script and issue a warning if there is mismatch, to eventually make things easier for both people using case sensitive file systems and people overlooking that they are changing the namespace by the way they wrote the reference.

I think the VS issue (need #I directive) is a higher priority to fix, the work around is not needed from the command line so experience is not consistent.

For the casing stuff, compiler should identify potential mismatch and help the user to write a script which will work disregarding filesystem and how the reference is brought, mostly through better warning / diagnostics from both command line tools and IDE integration. This seems less urgent.

Can we agree on first bug reproduced?

Do we want to move later issue to another ticket to try to clarify the different solutions to help users?

Thanks all, and sorry for the agitation.

@smoothdeveloper
Copy link
Contributor Author

Also found that the module name will have a first letter being capitalized whichever way you #load the script, I think this adds opportunity for trip up with all the casing stuff.

I know this deals with potential breaking changes, but VS needs to be fixed (should not need #I SOURCE_DIRECTORY) and user need to be guided from both fsc/fsi and VS in a consistent fashion to make the script work disregarding file system case sensistivity.

@latkin latkin changed the title #r and #load resolution is broken when using relative paths (what am I supposed to use?) VS editor shows bogus errors when scripts use multi-hop #r and #load with relative paths Feb 27, 2015
@latkin
Copy link
Contributor

latkin commented Feb 27, 2015

Yes, the VS editor does show bogus squiggles here, and that's a bug. Thankfully, things work ok when you send the code to FSI, so it's purely an editor-level issue. I've updated the title of the bug to indicate this reduced scope. Thanks for bringing this up.

Request/discussion on redesigning how implicit modules are named belongs at https://fslang.uservoice.com/. I tend to agree that it would be more intuitive if module name came from canonical file name on disk, but making changes to the scheme now would be highly impactful/breaking to many, while benefitting relatively few (IMO).

@dsyme
Copy link
Contributor

dsyme commented Feb 28, 2015

@latkin can you try this repro? Basically, the #r in a.fsx is resolved relative to the location of c.fsx (in Visual Studio), or the place where fsi.exe is invoked from (when running fsi.exe directly). In both cases the user expects the location to be relative to a.fsx.

Adding #I __SOURCE_DIRECTORY__ to a.fsx fixes the problem.

AFAIK this has been a long standing problem since F# 2.0.

capture

capture

@smoothdeveloper
Copy link
Contributor Author

@latkin thanks for the update / requalification, hope it won't be too hard to fix.

For discussing the module name mangling thing, I'm wondering if uservoice is the place to start, my concerns are:

  • user voice has very bare comment formatting
  • it's mostly to gather votes for language / tooling features
  • the voting system avoids one to propose many ideas (both good and bad)

Isn't there a repository where RFC are being drafted, I remember seeing this with advanced stuff from @dsyme, I could draft a simple RFC on github exposing the issues, receive some community feedback, then once it's ready submit it to uservoice.

Such RFC will:

  • document the current state of things
  • propose a set of warnings that help user to know what's happening, and saves the user of running through same things I encountered
  • eventually propose a new behaviour for implicit module naming (breaking change), if it receives community approval, it's worth considering

I'd be happy with just warnings, I don't want others to spend time like I did because something surprising is happening behind the scene.

Beside all that which will eventually be great, maybe I can have a couple of minute feedback on those things:

  • getting location of compiled source file from within a type provider: is there any technical solution right now? can it be part of an effort to extend the type provider infrastructure?
  • issues with fsproj / msbuild, showing a bunch of errors when solution has projects at different level of nesting, have you ever seen this? do I need to try to figure out a repro in a smaller context it's in my day work solution, it has around 40 projects (don't blame me for this 😉 it was around 60 before...)

@smoothdeveloper
Copy link
Contributor Author

@latkin would you agree to remove the needs repro tag and put a bug one?

@latkin
Copy link
Contributor

latkin commented Mar 3, 2015

@dsyme I do repro with your example. However it works if you change from #r "b.dll" to #r "./b.dll" I have not had a chance to debug this yet.

@smoothdeveloper yes, I've added the bug tag.

This discussion has ballooned to encompass a variety of feature requests and bugs. Let's please use this issue to just cover the bug as stated in the title - VS tooling for F# scripts is not handling relative paths properly.

Per @dsyme's example, seems there is a related (but unique) bug in FSI.exe where non-relative paths are processed with incorrect logic. I've opened #293 to track this.

Everything else is feature requests or design change requests and will not be considered here. Those belong on UserVoice. You are free to write up richly-formatted Gist or other doc and link that from UserVoice.

@latkin latkin self-assigned this Mar 13, 2015
latkin added a commit to latkin/visualfsharp that referenced this issue Mar 13, 2015
@latkin latkin closed this as completed in 3ed163f Mar 19, 2015
@latkin latkin added the fixed label Mar 19, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants