How can I avoid "not in ecosystem" errors thrown by comma IDE? - raku

I have a test file in my module's t/ directory that does a use on a TestUtils files which has some variables and routines:
use Test;
use TestUtils;
plan 4;
check_cmd_output('No arguments');
check_cmd_output('Successfully ingested', test_md_file);
check_cmd_output('Successfully ingested', test_md_file, 1);
check_cmd_output('Successfully ingested', vimwiki_arg_sim());
It works fine. However, Comma was complaining about TestUtils not being found "in the ecosystem" as well as throwing errors with identifiers from the TestUtils module that it was not loading:
I was able to stop Comma from complaining by:
Exporting the identifiers in the TestUtils file
Fully qualifying the identifiers in my test file with something like: TestUtils::check_cmd_output(...)
Adding TestUtils to the provides hash in the META6.json file.
I'm thinking there is probably a better way. I tried doing stuff like use lib 't' and use lib '.' but that did not help. Thanks.

Comma doesn't handle this situation perfectly; it understands (following the IntelliJ platform naming conventions):
Source roots, which are used as roots for resolving use statements within the project (anything not resolved is assumed to be from the module ecosystem)
Test roots, where test files are found (used for, for example, being able to run tests in an xt directory)
These are, however, mutually exclusive. If the TestUtils module were to be placed in a lib directory inside of t, then that lib directory could be marked as a source root, and the symbols should be resolved.

Related

How do I access a module's symbol table dynamically at runtime in Raku?

I want to be able to pass my script the path to a .rakumod file, say <blah>/Mod.rakumod, and be able to access the symbol table as a hash as if I'd used the module instead:
The module:
$ cat Mod.rakumod
unit module Mod;
sub bag is export { ... }
use lib <dir-containing-Mod>
use Mod;
say Mod::EXPORT::.keys
works, as expected, returning (ALL DEFAULT).
On the other hand:
use lib <dir-containing-Mod>
require Mod;
say Mod::EXPORT::.keys
fails with
Could not find symbol '&EXPORT' in 'Mod'
in block <unit> at <blah>
This is despite the fact that even with require, say Mod::.keys does see EXPORT:
use lib <dir-containing-Mod>
require Mod;
say Mod::.keys
---
(EXPORT Mod)
I need to use require to make this dynamic, as I don't know which module I'll want.
I can actually think of one thing to do, but it is absolutely disgusting:
save my module name into a variable $mod
have my script write another script using that module:
my $mod = <whatever>
my $cd = qq:to/END/;
use v6;
use lib qq\|\$\*CWD\|;
use $mod;
say {$mod}::EXPORT::ALL::.keys;
END
'aux.p6'.IO.spurt($cd);
and then have the initial script call the auxiliary one:
shell("raku aux.p6")
What worked, per raiph's answer (which I've accepted and am here paraphrasing):
pass the path to the module and save it as $path;
use lib the relevant directory:
my $dir = $path.IO.dirname;
use lib $dir;
extract the plain file name:
my $modFile = S/(.*)\..*/$0/ with $path.IO.basename;
finally, require that and pull the .WHO trick from raiph's answer, slightly adapted:
require ::($modFile);
say ::("{$modFile}::EXPORT::ALL").WHO.keys;
Run with <script> <path> that returned (&bag) all right.
Actually, the above doesn't quite work: use lib $dir will fail saying $dir is empty, because use lib isn't dynamic.
So instead I am now resorting to the unappealing solution of
copying the module file to a temporary directory ./TMP
having called use './TMP';
and then removing that directory when done.
TL;DR Use dynamic symbol lookup to get the symbol of the package whose symbols you want at run-time; then .WHO to get its stash; then .keys to get that stash's symbols.
For example:
use lib '.';
require Mod;
say ::Mod::EXPORT::('ALL').WHO.keys; # (&bag)
I'm going to make breakfast. I'll elaborate later.

CMake: How to reuse source file definitions between targets without duplication?

In my CMake project, I have 2 targets (a static library and a shared library) that share the same source code files, like this:
add_library("${PROJECT_NAME}" STATIC
"${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.h"
"${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.cpp"
"${PROJECT_SOURCE_DIR}/include/calculator/core/Calculator.h"
"${PROJECT_SOURCE_DIR}/src/calculator/core/Calculator.cpp"
)
add_library("${PROJECT_NAME}-shared" SHARED
"${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.h"
"${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.cpp"
"${PROJECT_SOURCE_DIR}/include/calculator/core/Calculator.h"
"${PROJECT_SOURCE_DIR}/src/calculator/core/Calculator.cpp"
)
Obviously, there is a problem here: the sources definition is duplicated. It's hard to maintain and it's also error prone.
In order to avoid that, I'd like to create a CMake list variable so that the sources definition could be reused in both targets.
I've tried this but it doesn't work:
set(CALCULATOR_CORE_SOURCES_LIST
"${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.h"
"${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.cpp"
"${PROJECT_SOURCE_DIR}/include/calculator/core/Calculator.h"
"${PROJECT_SOURCE_DIR}/src/calculator/core/Calculator.cpp"
)
string(REPLACE ";" " " CALCULATOR_CORE_SOURCES "${CALCULATOR_CORE_SOURCES_LIST}")
add_library("${PROJECT_NAME}" STATIC ${CALCULATOR_CORE_SOURCES})
add_library("${PROJECT_NAME}-shared" SHARED ${CALCULATOR_CORE_SOURCES})
It fails with the error: Cannot find source file.
So... how could I reuse source file definitions between targets without this duplication? Is it possible to do this using lists or is there a better approach to solve this issue?
PS: I'm using CMake 3.15
Your code is close; you can simply use the CALCULATOR_CORE_SOURCES_LIST variable to add the group of sources/headers to both add_library() calls:
set(CALCULATOR_CORE_SOURCES_LIST
${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.h
${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.cpp
${PROJECT_SOURCE_DIR}/include/calculator/core/Calculator.h
${PROJECT_SOURCE_DIR}/src/calculator/core/Calculator.cpp
)
add_library(${PROJECT_NAME} STATIC ${CALCULATOR_CORE_SOURCES_LIST})
add_library(${PROJECT_NAME}-shared SHARED ${CALCULATOR_CORE_SOURCES_LIST})
If the group of sources is the same for both libraries, you should only need to define the list once. From the documentation, the set command will do the following:
Multiple arguments will be joined as a semicolon-separated list to form the actual variable value to be set.
Thus, this list can be passed to the add_library() calls. You also shouldn't need the quotes (") around each file.

How to organize Lua module path and write "require" calls without losing flexibility?

Say I have a project, whose folder structure looks like below:
| main.lua
|
|---<model> // this is a folder
| |a.lua
| |b.lua
|
|---<view>
|a.lua
|b.lua
model/a.lua requries model/b.lua: require "b"
view/a.lua requries view/b.lua: require "b"
main.lua requries files in model and view.
Now I have problem to get these modules loaded correctly. I know I can fix it by changing the require calls to:
model/a.lua: require "model.b"
view/a.lua: require "view.b"
But if I do that, I have to modify these files every time when I change the folder structure.
So my questions are:
How to fix the module path issue without hard code paths in module files?
Why Lua doesn't use the module search rule of Node.js, which looks easier?
When you require a module, the string parameter from require gets passed into the module which you can access using the variable-argument syntax .... You can use this to include other dependent modules which reside in the same path as the current module being requireed without making it dependent on a fixed hard-coded module name.
For your example, instead of doing:
-- model/a.lua
require "model.b"
and
-- view/a.lua
require "view.b"
You can do:
-- model/a.lua
local thispath = select('1', ...):match(".+%.") or ""
require(thispath.."b")
and
-- view/a.lua
local thispath = select('1', ...):match(".+%.") or ""
require(thispath.."b")
Now if you change directory structure, eg. move view to something like control/subcontrol/foobar, then control/subcontrol/foobar/a.lua (formerly view/a.lua) will now try to require control/subcontrol/foobar/b.lua instead and "do the right thing".
Of course main.lua will still need to fully qualify the paths since you need some way to disambiguate between model/a.lua and view/a.lua.
How to fix the module path issue without hard code paths in module files?
I don't have any better cross-platform solution, maybe you should plan the folder structure early on.
Why Lua doesn't use the module search rule of Node.js, which looks easier?
Because Lua tries its best to rely only on ANSI C, which is really successful. And in ANSI C, there's no such concept of directories.
There are a couple approaches you can use.
You can add relative paths to package.path as in this SO answer. In your case you'd want to add paths in main.lua that correspond to the various ways you might access the files. This keeps all the changes required when changing your directory structure local to one file.
You can add absolute paths to package.pathusing debug.getinfo -- this may be a little easier since you don't need to account for all the relative accesses, but you still need to do this in main.lua when changing your directory structure, and you need to do string manipulation on the value returned by debug.getinfo to strip the module name and add the subdirectory names.
> lunit = require "lunit"
> info = debug.getinfo(lunit.run, "S")
> =info.source
#/usr/local/share/lua/5.2/lunit.lua
> =info.short_src
/usr/local/share/lua/5.2/lunit.lua
The solution is to add the folder of main.lua (project root) to package.path in main.lua.
A naive way to support folders of 1 level deep:
-- main.lua
package.path = package.path .. ";../?.lua"
Note for requires in (project root) will look up files outside of project root, which is not desirable.
A better way of to use some library (e.g.: paths, penlight) to resolve the absolute path and add it instead:
-- main.lua
local projectRoot = lib.abspath(".")
package.path = package.path .. ";" .. projectRoot .. "/?.lua"
Then in you source use the folder name to scope the files:
-- model/a.lua
require "model.b"
-- you can even do this
require "view.b"
and
-- view/a.lua
require "view.b"

Why won't MSBuild build a project with a dot in the name?

The Story So Far
I've got a nice solution with a desktop application project, a few library projects, and a couple of development tools projects (also desktop applications). At the moment, my build server outputs all of the code into one OutputPath. So we end up with
drop-x.y.z\
Company.MainApplication.exe <-- main application
Company.MainApplicationCore.dll <-- libraries
Helper.exe <-- developer tools
Grapher.exe
Parser.exe
... <-- the rest of the output
But, we're growing up and people outside of our team want access to our tools. So I want to organize the output. I decided that what we would want is a different OutputPath per executable project
drop-x.y.z\
Company.MainApplication\
Company.MainApplication.exe <-- main application
Company.MainApplicationCore.dll <-- libraries
... <-- application specific output
Helper\
Helper.exe <-- developer tools
... <-- tool specific output
Grapher\
Grapher.exe
...
Parser\
Parser.exe
...
What I Did
I found this simple command. I like it because it retains all the Solution working-dir context that makes msbuild a pain.
msbuild /target:<ProjectName>
For example, from my solution root as a working directory, I would call
PS> msbuild /target:Helper /property:OutputPath="$pwd\out\Helper"
I'm testing this from PowerShell, so that $pwd resolves to the full path to my working directory, or the Solution root in this case. I get the output I desire.
However, when I run this command
PS> msbuild /target:Company.MainApplication /property:OutputPath="$pwd\out\Company.MainApplication"
I get the following error output (there's no more information, I ran with /verbosity:diagnostic)
The target "Company.MainApplication" does not exist in the project.
What I Need
The command fails on any project with a dot or dots in the name. I tried with many combinations of working directories and properties. I tried several ways of escaping the property values. I also tried running the command from a <Task> in a targets file.
I need to know either
A) How to fix this command to work property
B) How to achieve the same output with minimal friction
Try using an underscore as an escape character for the dot in the target parameter, e.g.
msbuild /target:Company_MainApplication /property:OutputPath="$pwd\out\Company.MainApplication"
Specify the target after the -target: switch in the format :. If the project name contains any of the characters %, $, #, ;, ., (, ), or ', replace them with an _ in the specified target name.
https://learn.microsoft.com/en-us/visualstudio/msbuild/how-to-build-specific-targets-in-solutions-by-using-msbuild-exe?view=vs-2019
Dan Nolan's answer and comments are correct. Just want to supplement the Microsoft documentation.
The /targets: switch is to identify a <Target to run in the project file. You need to supply your .csproj file as a an argument that is not prefixed by a /xx option marker.
You might also want to work based on the .sln file. In that case, you still dont specify the project in the .sln to build in this manner. I'll leave you to search up the correct syntax in case that's what you end up doing.

How to abort processing CMakeLists for current directory

I have a project structure like:
src/CMakeLists.txt
src/test/component1/CMakeLists.txt
src/test/component2/CMakeLists.txt
For the testing, I'm using Qt - however, I want to make sure that if Qt (or some other test-specific package is not found) I simply skip the package.
I tried
find_package(Qt4 QUIET COMPONENTS QtCore QtTestLib)
if (NOT QT4_FOUND)
message(SEND_ERROR "Qt4 not found - skipping building tests")
endif (NOT QT4_FOUND)
but that doesn't work like I want to since that still prevents the generation of the Makefiles. The only way I can think is to put the entire body of the CMakeLists file into the body of the conditional.
Is there a way to say "skip processing the remainder of this CMakeLists"?
From the CMake documentation
return: Return from a directory or function.
return()
Returns from a directory or function.
When this command is encountered, it
caused process of the current function
or directory to stop and control is
return to the caller of the function,
or the parent directory if any. Note
that a macro is not a function and
does not handle return like a function
does.