LINUX.ORG.RU

[F#] CodeProvider and CodeDom.Compiler


0

1

Веселюсь с компиляцией в рантайме. Похоже она нынче не поддерживается, что же теперь делать, если надо.

В compile_file CodeCompiler() конструктора банально нет, или я что-то совсем не понимаю.

В compile_file2 ещё лучше - в строке

let res = pro.CompileAssemblyFromFile(opt, path)
вываливается с ошибкой, что нет файла(причём непонятно какого, то ли path, то ли output). path точно есть, с элементарной структурой:
module Test
let sum a b = a + b
output пробовал создавать руками, ничего не меняется.

module TestLoader
open System.IO
open System.CodeDom.Compiler
open Microsoft.FSharp.Compiler.CodeDom
open InternalDSLTypes
open InternalDSL
open Sunbox
open CheckFunctions
open System.CodeDom.Compiler
    
type System.CodeDom.Compiler.ICodeCompiler with 
    member this.CompileAssemblyFromFile 
            (options:CompilerParameters,fileName:string) : CompilerResults = 
        this.CompileAssemblyFromFileBatch(options, [|fileName|])
(*        
let compile_file str assemblies output =
    let pro = new System.CodeDom.Compiler.CodeCompiler()
    let opt = CompilerParameters(assemblies, output)
    let res = pro.CompileAssemblyFromFile(opt, str)
    if res.Errors.Count = 0 then 
         Some(FileInfo(res.PathToAssembly)) 
    else None
*)
  
let compile_file2 path use_asms output =
    use pro = new FSharpCodeProvider()
    let opt = CompilerParameters(use_asms, output)
    let res = pro.CompileAssemblyFromFile(opt, path)
    if res.Errors.Count = 0 
    then Some(FileInfo(res.PathToAssembly))
    else None     

let path = "D:/Sources/ATProjectChecker/ATProjectChecker/tests/test2.fs"
let out = "D:/Sources/ATProjectChecker/ATProjectChecker/tests/test.dll"
let asms = [||]
let res = compile_file2 [|path|] asms out

попробовал по-другому, компилирую отдельно нужный файл и подгружаю с помощью рефлекции.

  • целевой файл для загрузки в рантайм - test.fs
  • проект, в котором объявлены модули, используемые в test.fs - A.exe
  • проект, в который надо загрузить test.fs в рантайме - A.exe

test.fs(ф-ция test делает в итоге Sunbox.root <- новый объект типа Test):

(*
fsc --target:library test.fs -r A.exe
*)

module Test
open Caps
open CheckFunctions
open InternalDSL
open InternalDSLTypes
open ProjectTypes
open Sunbox

[<EntryPoint>]
test "My simple test" starting "26.02.2012"
    whenSuccess ["Inner simple test"]
    whenWrong ["Some other test"; "And other..."]

let get_root () =
    (*test "My simple test" starting "26.02.2012"
        whenSuccess ["Inner simple test"]
        whenWrong ["Some other test"; "And other..."]
    *)
    Sunbox.root
Код из A.exe:
let path = "D:/Sources/ATProjectChecker/ATProjectChecker/tests/test.dll"
let asm = System.Reflection.Assembly.LoadFile path
let omg = asm.GetType("Test").GetMethod("get_root")
let f() = omg.Invoke(null, [||]) :?> Test
let res = f()

Sunbox.root это объект в модуле Sunbox, который в A.exe. Так вот, как можно обойтись без ф-ции get_root чтобы получить root, изменённый ф-цией test

pseudo-cat ★★★
() автор топика
Ответ на: комментарий от O02eg

просто с работы пишу, дома тот же код запускаю под mono, используя те же виндовские компиляторы, так что спорно

pseudo-cat ★★★
() автор топика
Ответ на: комментарий от pseudo-cat

к тому же здесь, по-моему, самые адекватные F#-овцы, т.е. люди, использующие F#, не знаю как кратко и необидно сказать)

pseudo-cat ★★★
() автор топика

CodeDom не зависит от F#. Все настраивается провайдерами. Если возникают сомнения, то можно подсунуть код на C#, взяв соответствующий провайдер. Для C# должно работать железо-бетонно, иначе бы не было ASP.NET.

Хотя провайдер F# тоже должен работать. Код он, конечно, генерит ужасный, но у меня работал во всяком случае.

В общем, в твой код я сильно не вдавался - просто больше двух лет не работал с CodeDom, да и лень вспоминать. Могу лишь предложить поменять / на \ в путях. Если это не поможет, то попробовать скормить код на C#, как я упомянул выше. Надеюсь, что это прояснит ситуацию.

dave ★★★★★
()
Ответ на: комментарий от dave

C# CodeProvider работает нормально, это мне и раньше было известно)

Короче, пока что решил сделать небольшой костыль - вызывать компиллер руками через Process.Start

pseudo-cat ★★★
() автор топика
Ответ на: комментарий от pseudo-cat

Хочешь сказать, что на одной и той же задаче при практически тех же входных данных (меняем расширение .fs на .cs) провайдер F# сбоит, а провайдер C# работает правильно? Не верю.

dave ★★★★★
()

А, кстати, FSharp.Core.dll не надо подключать к CompilerParameters.ReferencedAssemblies? По идее должен схватить и так, но кто знает?

Наконец, можно сгенерировать в памяти (CompilerParameters.GenerateInMemory = true) и тут же подгрузить. Правда, это засирает память текущего application domain. Оттуда такой код не выгрузишь - только вместе с доменом и то вручную (за что люблю java).

dave ★★★★★
()
Ответ на: комментарий от dave

Впрочем, память application domain при любом виде загрузки dll засирается - специфика .NET. Ничего не поделаешь...

dave ★★★★★
()
Ответ на: комментарий от dave

провайдер C# работает с C# кодом, так смысл сравнивать его с работой F# провайдера?

на stackoverflow предложили добавить сорцы PowerPack в проект и посмотреть что именно происходит, попробую после праздников - отпишусь

pseudo-cat ★★★
() автор топика
Ответ на: комментарий от dave

а вот это уже хреного, у меня могут быть сотни ассемблей, подгружаемых в рантайме, хотя они и небольшого размера, но всё же

pseudo-cat ★★★
() автор топика
Ответ на: комментарий от pseudo-cat

А я забил на это :) У меня генерируется код на CodeDom, компилируется в памяти, а потом подгружается в единственный в моем приложении application domain. И так много раз. Поскольку приложение у меня десктопное, то это некритично. Проще перезагрузить приложение.

.NET - это не лисп и даже не java. Все, что ты загрузил в application domain, можно выгрузить только вручную вместе с самим domain. А для обмена данными между доменами, нужна определенная сериализация, что затратно. Увы. По крайнем мере, так было во времена .NET 1.1 и 2.0. Вряд ли потом что-то изменилось.

Но иногда в зависимости от задачи можно создать временный домен, загрузить туда код, а потом вызывать код из другого домена. Как я уже написал, такие вызовы дороги, но если их мало, то вполне нормально. Потом этот временный домен можно явно выгрузить вместе со всем загруженным туда кодом. Но мне такий вариант не подходил, поскольку загруженный код вызывался очень часто, да и данных было много.

Что касается твоей задачи. Если c C# работает, то возможная причина в том, что нужна ссылка на FSharp.Core.dll, хотя я не уверен. Причин может быть много. Твой код не прогонял, только пробежал взглядом.

dave ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.