August 25, 2021
On 8/25/2021 2:05 PM, Paul Backus wrote:
> The problem with -main is that it's too blunt an instrument: it will add a main function whether the code you're compiling already has one or not. That means you have to keep track of which modules have main functions and which ones don't, so that you (or more realistically, your build system) can add -main only when it's necessary.

That problem has never occurred to me, and has never happened to me, yet I use -main all the time.

One way to resolve it is just put your main() in a separate module.
August 25, 2021
On Wed, Aug 25, 2021 at 03:44:40PM -0700, Walter Bright via Digitalmars-d wrote:
> On 8/25/2021 2:05 PM, Paul Backus wrote:
> > The problem with -main is that it's too blunt an instrument: it will add a main function whether the code you're compiling already has one or not. That means you have to keep track of which modules have main functions and which ones don't, so that you (or more realistically, your build system) can add -main only when it's necessary.
> 
> That problem has never occurred to me, and has never happened to me, yet I use -main all the time.
> 
> One way to resolve it is just put your main() in a separate module.

This always happens to me, so much so that I've come to insert the following as boilerplate to all my code:

	version(unittest){} else
	int main(string[] args) { ... }


T

-- 
LINUX = Lousy Interface for Nefarious Unix Xenophobes.
August 25, 2021
On 8/25/2021 2:46 PM, Paul Backus wrote:
>>>>     dmd -i -unittest -main=ifMissing -run mymodule.d

The trouble with adding switches like that is it takes a while to understand them and what they do, and they'll inevitably create yet another problem which will require another switch.

It's better to use one of the existing methods shown in this thread which use already well-understood behavior.
August 25, 2021
On 8/25/2021 3:48 PM, H. S. Teoh wrote:
> This always happens to me, so much so that I've come to insert the
> following as boilerplate to all my code:
> 
> 	version(unittest){} else
> 	int main(string[] args) { ... }


That works, too.
August 26, 2021

On Wednesday, 25 August 2021 at 22:44:40 UTC, Walter Bright wrote:

>

On 8/25/2021 2:05 PM, Paul Backus wrote:

>

The problem with -main is that it's too blunt an instrument: it will add a main function whether the code you're compiling already has one or not. That means you have to keep track of which modules have main functions and which ones don't, so that you (or more realistically, your build system) can add -main only when it's necessary.

That problem has never occurred to me, and has never happened to me, yet I use -main all the time.

One way to resolve it is just put your main() in a separate module.

What if you are dealing with something that is a single file?

If you run below on run.dlang.org with either -unittest or -unittest -main, then you get a message that only one main is allowed. If you remove the dependency, then it compiles without error with -unittest but gets the same error with -unittest -main. The difference is (IIRC) when you include a dependency it is also calling -single. If you had something like -main=ifmissing that others describe, then there would be a consistent solution that could work well with run.dlang.org.

/+dub.sdl:
dependency "mir-algorithm" versio="*"
+/

unittest {}
void main() {}
August 26, 2021

On Wednesday, 25 August 2021 at 11:11:24 UTC, Steven Schveighoffer wrote:

>

Disagree. I'm fine with the simple unittest experience, and avoiding putting all this kind of bloat into the runtime.

-Steve

What bloat ? Is it the amount of code needed, or the binary size that bothers you ?

For the former, we already have many framework doing this, so there's a real interest in having it, and having it in a single place means that improvements will be centralized, instead of being duplicated. So, less code bloat when you look at the big picture.

For the later, I've never seen anyone bother about unittest binary size. I don't think we should. And if the unittest framework code ends up in the binary when -unittest is not used, we're doing it wrong.

August 26, 2021
On Wednesday, 25 August 2021 at 20:49:32 UTC, Walter Bright wrote:
> On 8/24/2021 5:21 AM, deadalnix wrote:
>> Adding a main for libraries is required, or it won't link, but adding one to executable is going to cause a link error.
>
> This is indeed a real problem. It was solved with the -main switch to dmd, which will just stick one in for you. I use it all the time for unit testing modules.

No it wasn't, because it add a main no matter what, which means that when there is a main in the module being tested, you get a link error.

The root of the problem, really, is that I don't cares about main when running unit tests. I don't want to know if it is there or not, I don't want it to run, nothing. If the compiler needs one to link under the hood, then good for it, but this is an implementation detail.

When running the unit tests, one wants to know if the unit tests pass. Nothing more, nothing less.

The compiler has 100% of the information it needs to make this happen, yet, for some reason, it is requesting them from me. Which i can provide too, but this is fragile, and it doesn't scale, as I can't get my build system to automate it for me. Which in turn lead to most projects rolling out their own solution.

I can work with having to pass the -main flag. This is not ideal, because what else could I possibly want? A link error? I'm not trying to link and run main. I care about unittests in that case. But having main declared twice is some special level of nonsense. In what case could I possibly want two main?
August 26, 2021

On Thursday, 26 August 2021 at 09:38:54 UTC, deadalnix wrote:

>

The root of the problem, really, is that I don't cares about main when running unit tests. I don't want to know if it is there or not, I don't want it to run, nothing. If the compiler needs one to link under the hood, then good for it, but this is an implementation detail.

OK. Create a bugzilla issue. There are some related bugs that someone might want to fix at the same time (#20340 it doesn't use a betterC main when -betterC is passed, #16440 it breaks with -c and -of).

This is about the extent of the current support, in src/dmd/mars.d's tryMain:

    if (params.addMain)
        files.push("__main.d");
    // Create Modules
    Modules modules = createModules(files, libmodules);
    // Read files
    // Start by "reading" the special files (__main.d, __stdin.d)
    foreach (m; modules)
    {
        if (params.addMain && m.srcfile.toString() == "__main.d")
        {
            auto data = arraydup("int main(){return 0;}\0\0\0\0"); // need 2 trailing nulls for sentinel and 2 for lexer
            m.srcBuffer = new FileBuffer(cast(ubyte[]) data[0 .. $-4]);
        }

and src/dmd/glue.d:

private bool onlyOneMain(Loc loc)
{
    __gshared Loc lastLoc;
    __gshared bool hasMain = false;
    if (hasMain)
    {
        const(char)* msg = "";
        if (global.params.addMain)
            msg = ", -main switch added another `main()`";

It's not surprising that such a lightweight implementation doesn't result in world-class ergonomics. It's something I use all the time to test individual modules at the CLI, and I'd much rather have this than nothing. But for bells and whistles I'd look to dub.

In a pinch you could write a dmd wrapper that calls dmd, sees if it gets a linker complaint about main, and if so calls dmd again with -main. An obvious performance hit, probably not that bad in most cases where you would consider this, but now you don't have care about main.

#! /usr/bin/env rdmd
import std.process : execute, spawnProcess, wait;
import std.algorithm : canFind;
import std.stdio : write;

int main(string[] args) {
    auto result = execute(["dmd"] ~ args[1..$]);
    if (result.status != 0 && result.output.canFind("undefined reference to `main'")) {
        return spawnProcess(["dmd", "-main"] ~ args[1..$]).wait;
    }
    write(result.output);
    return result.status;
}

usage:

$ cat nomain.d
unittest {
    import std.stdio : writeln;

    writeln("no main");
}
$ cat hasmain.d
unittest {
    import std.stdio : writeln;

    writeln("has main");
}

void main() {}
$ ./automain.d -unittest -run nomain.d
no main
1 modules passed unittests
$ ./automain.d -unittest -run hasmain.d
1 modules passed unittests
has main
August 26, 2021

On Thursday, 26 August 2021 at 11:32:06 UTC, jfondren wrote:

>

On Thursday, 26 August 2021 at 09:38:54 UTC, deadalnix wrote:

>

The root of the problem, really, is that I don't cares about main when running unit tests. I don't want to know if it is there or not, I don't want it to run, nothing. If the compiler needs one to link under the hood, then good for it, but this is an implementation detail.

OK. Create a bugzilla issue. There are some related bugs that someone might want to fix at the same time (#20340 it doesn't use a betterC main when -betterC is passed, #16440 it breaks with -c and -of).

This is about the extent of the current support, in src/dmd/mars.d's tryMain:

    if (params.addMain)
        files.push("__main.d");
    // Create Modules
    Modules modules = createModules(files, libmodules);
    // Read files
    // Start by "reading" the special files (__main.d, __stdin.d)
    foreach (m; modules)
    {
        if (params.addMain && m.srcfile.toString() == "__main.d")
        {
            auto data = arraydup("int main(){return 0;}\0\0\0\0"); // need 2 trailing nulls for sentinel and 2 for lexer
            m.srcBuffer = new FileBuffer(cast(ubyte[]) data[0 .. $-4]);
        }

and src/dmd/glue.d:

private bool onlyOneMain(Loc loc)
{
    __gshared Loc lastLoc;
    __gshared bool hasMain = false;
    if (hasMain)
    {
        const(char)* msg = "";
        if (global.params.addMain)
            msg = ", -main switch added another `main()`";

It's not surprising that such a lightweight implementation doesn't result in world-class ergonomics. It's something I use all the time to test individual modules at the CLI, and I'd much rather have this than nothing. But for bells and whistles I'd look to dub.

In a pinch you could write a dmd wrapper that calls dmd, sees if it gets a linker complaint about main, and if so calls dmd again with -main. An obvious performance hit, probably not that bad in most cases where you would consider this, but now you don't have care about main.

[...]

Congratulations, you just wrote the embryo of a test framework. It will work, and indeed, most projects ou there get one past a certain size.

It' very unfortunate though, because the compiler has all the info it needs to do the right thing, and yet refuses to do it.

Note that you could likely get better perfs out of this by linking in a weak definition of _Dmain, so it won't clash, but the general problem remains.

August 26, 2021

On Thursday, 26 August 2021 at 09:38:54 UTC, deadalnix wrote:

>

But having main declared twice is some special level of nonsense. In what case could I possibly want two main?

When there's both a main function and the -main flag, should -main do nothing or override the main function with an empty one?