oleg-shilo / cs-script

C# scripting platform
http://www.cs-script.net
MIT License
1.57k stars 234 forks source link

Add posibility to use variables in css_include and css_import statements #310

Closed rzamponiAtIgt closed 1 year ago

rzamponiAtIgt commented 1 year ago

In an hosted environment I would like to use path variables (e.g //css_include $(SomeDirectoryPath)/my_script.csx) for the directories of the included script. Like you could do in MsBuild with properties.

The easiest approach would be to add a public delegate property to the CoreExtensions class which the Expand method invokes before invoking Environment.ExpandEnvironmentVariables. That would enable users to define their own implementation of variable expansion.

oleg-shilo commented 1 year ago

The environment variables are already supported. It's just a little different syntax:

//css_include %my_script%
rzamponiAtIgt commented 1 year ago

I know about the environment variables. But this expands only what is in the environment. In my host application I want to expand variables which are only available in the context of the host application.

oleg-shilo commented 1 year ago

But the compiler does not have access to the context of your host application. YOu may want then to insert a //css_include $(SomeDirectoryPath).... line at the start of your script

oleg-shilo commented 1 year ago

This is how any scripting runtime works. You will have the same challenge if you use Python for example.

rzamponiAtIgt commented 1 year ago

But the compiler does not have access to the context of your host application. YOu may want then to insert a //css_include $(SomeDirectoryPath).... line at the start of your script

This is exactly the location I want to do this. But I have to also tell the cs-script library how to expand it.

This is how any scripting runtime works. You will have the same challenge if you use Python for example.

Actually in python scripts you can invoke callback functions to update the path before doing the imports.

oleg-shilo commented 1 year ago

This is exactly the location I want to do this. But I have to also tell the cs-script library how to expand it.

Yes, but I meant that you modify the script by injecting the first line with fully defined absolute path.

Though you can achieve the desired behaviour by using the same envars channel for the. You just tunnel your "variables" through the process context, which envars are: :

yours script:

//css_include %SomeDirectoryPath%/my_script.csx)
. . .

your host app:

var SomeDirectoryPath = @"<script dir path>";

Environment.SetEnvironmentVariable(nameof(SomeDirectoryPath), SomeDirectoryPath);

dynamic script = CSScript.Evaluator.Load(@"some script"); 
. . .

Is it what you want to achieve?

Actually in python scripts you can invoke callback functions to update the path before doing the imports. Interesting, was not aware of this feature. But it is not what you are trying to achieve. You want from script access the value of the variable. At least it is how I read your scenario. Callback does the opposite it lats define from the host the value of the variable in the imports statement of the script.

rzamponiAtIgt commented 1 year ago

[Snipped answer body] Is it what you want to achieve?

Partially. We distribute reusable scripts via NuGet packages. As this packages not only contain c# scripts but also python and other formats, the host application is responsible to resolve the packages. The package.props files define properties to locate and identify the content of a package. Of course I could ask the developers to add another MsBuild item to identify all the properties which need to added to the process environment variable but I think it is less error prone to simply resolve the property when being asked for it. Also adding additional probingDirs would not make any sense, as this could result in unintended behavior if some scripts have the same name.

In my case I've changed Expand in src/cscs/Utils/CoreExtensions.cs now to enable me to override how the expansion works.

        public static Func<string, string> ExpandFunction { get; set; }

        internal static string Expand(this string text) => ExpandFunction?.Invoke(text) ?? Environment.ExpandEnvironmentVariables(text);

        internal static string UnescapeExpandTrim(this string text) =>
            CSharpParser.UnescapeDirectiveDelimiters(Expand(text)).Trim();

In my opinion the only hacky part on this solution is, that I added the public property ExpandFunction to an utility class instead of using the way over the configuration (just for being under time pressure). From my point of view making this method configurable could be beneficial for other users as well because it would make the css_xxx imports more flexible for custom host application needs.

What do you think? I could also provide a pull request if you like. Maybe lay out a hint where you would do the setting of ExpandFunction if it was provided via CSScript.EvaluatorConfig.

Actually in python scripts you can invoke callback functions to update the path before doing the imports.

Interesting, was not aware of this feature. But it is not what you are trying to achieve. You want from script access the value of the variable. At least it is how I read your scenario.

Have a read about conditional imports. I think this is the one feature which makes python really aspect oriented 😊 You are right that was not my exact scenario.

As far as I understand the cs-script implementation the cs_xxx commands are not part of the script itself but a meta description for cs-script to enable the roslyn compiler to do its job.

oleg-shilo commented 1 year ago

I could also provide a pull request if you like...

Yes, please do so. I like your proposal after all. When I see the whole picture I do see the value. I will your PR as a baseline and maybe move it to another class if it makes it more natural to use.

As far as I understand the cs-script implementation the cs_xxx commands are not part of the script itself but a meta description for cs-script to enable the roslyn compiler to do its job.

Yes but not for Roslyn. Roslyn cannot do more than a single C# document. It is CS-Script engine who does the work.