dub icon indicating copy to clipboard operation
dub copied to clipboard

Configuration "unittest" leads to error "only one main... allowed"

Open andre2007 opened this issue 6 years ago • 9 comments

I use configuration "unittest" to add a dependency to a unittest framework. But there is some strange behavior which an unexperienced developer cannot understand.

Execute command: dub init bug1

Add configurations to dub.json

	"configurations": [
		{
			"name": "debug"
		},
		{
			"name": "unittest"

		}
	]

Issue 1: Although the package contains an app.d file, running command "dub test" will detect the targetType "library". Target type library will add itself a void main method which will now lead to error:

Generating test runner configuration 'bug1-test-unittest' for 'unittest' (library).
...
source\app.d(3,6): Error: only one main/WinMain/DllMain allowed. Previously found main at ..\..\AppData\Local\Temp\dub_test_root-4ddc4093-e161-491c-b61b-b8f1c8b719fd.d(9,12)

There is no information, that the second main method was generated by dub. Although why is target type "library" detected?

Issue 2:: Attribute mainSourceFile is quite hard to understand. By adding this attribute, app.d is excluded from the compilation (...Excluding main source file source/app.d from test...)

{
			"name": "unittest",
			"mainSourceFile": "source/app.d"
}

What is hard to understand is following: If I now also add attribute "targetType = executable" then mainSourceFile attribute is ignored and app.d isn't excluded anymore.

If this behavior is the expected / designed behavior, could you please add some sentences on the documentation (https://code.dlang.org/package-format?lang=json) explaining the mechanics: unittest > library -> auto generated main -> mainSourceFile -> targetType Executable

andre2007 avatar Oct 18 '17 19:10 andre2007

This is quite annoying if all one wants to do is to e.g. simply start a script before/after building a unittest. Adding the "unittest" configuration somehow breaks the "normal" unittest behaviour. See below example.

$ dub --version
DUB version 1.9.0, built on Jun  7 2018

Minimal example

source/app.d:

void main(){}

dub.sdl:

name "test"

Output of dub test --verbose (succeeds):

Using dub registry url 'https://code.dlang.org/'
Refreshing local packages (refresh existing: true)...
Looking for local package map at /var/lib/dub/packages/local-packages.json
Looking for local package map at /Users/Timoses/.dub/packages/local-packages.json
Try to load local package map at /Users/Timoses/.dub/packages/local-packages.json
Ignoring version specification (>=0.0.0) for path based dependency .
Ignoring version specification (>=0.0.0) for path based dependency .
Ignoring version specification (>=0.0.0) for path based dependency .
Ignoring version specification (>=0.0.0) for path based dependency .
Ignoring version specification (>=0.0.0) for path based dependency .
Ignoring version specification (>=0.0.0) for path based dependency .
Note: Failed to determine version of package mytest at .. Assuming ~master.
Refreshing local packages (refresh existing: false)...
Looking for local package map at /var/lib/dub/packages/local-packages.json
Looking for local package map at /Users/Timoses/.dub/packages/local-packages.json
Try to load local package map at /Users/Timoses/.dub/packages/local-packages.json
Refreshing local packages (refresh existing: false)...
Looking for local package map at /var/lib/dub/packages/local-packages.json
Looking for local package map at /Users/Timoses/.dub/packages/local-packages.json
Try to load local package map at /Users/Timoses/.dub/packages/local-packages.json
Checking for upgrades.
Using cached upgrade results...
No source files found in configuration 'library'. Falling back to "dub -b unittest".
Configuring dependent mytest, deps:
Performing "unittest" build using /Library/D/dmd/bin/dmd for x86_64.
mytest ~master: building configuration "application"...
/Library/D/dmd/bin/dmd -c -of.dub/build/application-unittest-posix.osx-x86_64-dmd_2080-6878B1D82B97B3507BAD24C0266CCBB5/mytest.o -debug -g -unittest -w -version=Have_mytest -Isource/ source/app.d -vcolumns
Linking...
/Library/D/dmd/bin/dmd -of.dub/build/application-unittest-posix.osx-x86_64-dmd_2080-6878B1D82B97B3507BAD24C0266CCBB5/mytest .dub/build/application-unittest-posix.osx-x86_64-dmd_2080-6878B1D82B97B3507BAD24C0266CCBB5/mytest.o -g
Copying target from /Users/Timoses/programs/dotfim/mytest/.dub/build/application-unittest-posix.osx-x86_64-dmd_2080-6878B1D82B97B3507BAD24C0266CCBB5/mytest to /Users/Timoses/programs/dotfim/mytest
Running ./mytest 

Now let's break it

Change dub.sdl to:

name "test"
configuration "unittest" {
    preBuildCommands "echo \"HELLO\""
}

Output of dub test --verbose (fails):

Using dub registry url 'https://code.dlang.org/'
Refreshing local packages (refresh existing: true)...
Looking for local package map at /var/lib/dub/packages/local-packages.json
Looking for local package map at /Users/Timoses/.dub/packages/local-packages.json
Try to load local package map at /Users/Timoses/.dub/packages/local-packages.json
Ignoring version specification (>=0.0.0) for path based dependency .
Ignoring version specification (>=0.0.0) for path based dependency .
Ignoring version specification (>=0.0.0) for path based dependency .
Ignoring version specification (>=0.0.0) for path based dependency .
Ignoring version specification (>=0.0.0) for path based dependency .
Ignoring version specification (>=0.0.0) for path based dependency .
Note: Failed to determine version of package mytest at .. Assuming ~master.
Refreshing local packages (refresh existing: false)...
Looking for local package map at /var/lib/dub/packages/local-packages.json
Looking for local package map at /Users/Timoses/.dub/packages/local-packages.json
Try to load local package map at /Users/Timoses/.dub/packages/local-packages.json
Refreshing local packages (refresh existing: false)...
Looking for local package map at /var/lib/dub/packages/local-packages.json
Looking for local package map at /Users/Timoses/.dub/packages/local-packages.json
Try to load local package map at /Users/Timoses/.dub/packages/local-packages.json
Checking for upgrades.
Using cached upgrade results...
Generating test runner configuration 'mytest-test-unittest' for 'unittest' (library).
Get module name from path: /Users/Timoses/programs/dotfim/mytest/source/app.d
Refreshing local packages (refresh existing: false)...
Looking for local package map at /var/lib/dub/packages/local-packages.json
Looking for local package map at /Users/Timoses/.dub/packages/local-packages.json
Try to load local package map at /Users/Timoses/.dub/packages/local-packages.json
Configuring dependent mytest, deps:
Performing "unittest" build using /Library/D/dmd/bin/dmd for x86_64.
mytest ~master: building configuration "mytest-test-unittest"...
Running pre-build commands...
Running echo "HELLO"
HELLO
/Library/D/dmd/bin/dmd -c -of.dub/build/mytest-test-unittest-unittest-posix.osx-x86_64-dmd_2080-EBCD2572FBC9DD092DC8E29B010CF88C/mytest-test-unittest.o -debug -g -unittest -w -version=VibeCustomMain -version=Have_mytest -Isource/ /var/folders/xf/n9z8fjbd0tx4l6t3h1l1n4_00000gn/T/dub_test_root-286ff5ba-2ea5-4569-8532-bb47822835eb.d source/app.d -vcolumns
source/app.d(1,6): Error: only one `main` allowed. Previously found `main` at /var/folders/xf/n9z8fjbd0tx4l6t3h1l1n4_00000gn/T/dub_test_root-286ff5ba-2ea5-4569-8532-bb47822835eb.d(9,12)
FAIL .dub/build/mytest-test-unittest-unittest-posix.osx-x86_64-dmd_2080-EBCD2572FBC9DD092DC8E29B010CF88C/ mytest-test-unittest executable
/Library/D/dmd/bin/dmd failed with exit code 1.

In the working case the output shows

No source files found in configuration 'library'. Falling back to "dub -b unittest".

whereas that is not shown in the non-working output.

The behaviour feels very opaque. Apparently, specifying the unittest configuration overwrites the behaviour.

One could specify in dub.sdl:

configuration "unittest" {
    buildOptions "unittests"
}

However, that still fails with "only one main allowed. ...". Additionally it tells that user:

## Warning for package mytest, configuration unittest ##

The following compiler flags have been specified in the package description
file. They are handled by DUB and direct use in packages is discouraged.
Alternatively, you can set the DFLAGS environment variable to pass custom flags
to the compiler, or use one of the suggestions below:

unittests: Call DUB with --build=unittest

Well.. How then should a custom "unittest" configuration be set up? And what are the quirks behind the scene that make dub test work in one case but not in the other?

Timoses avatar Dec 20 '18 14:12 Timoses

Bump! Same issue. Is this going to be fixed? How should one trigger unittests in dub?

tastyminerals avatar Aug 11 '21 09:08 tastyminerals

Use mainSourceFile in your configuration. It will be automatically excluded from other configurations.

Geod24 avatar Aug 11 '21 09:08 Geod24

Use mainSourceFile in your configuration. It will be automatically excluded from other configurations.

Unfortunately, it doesn't work with dub test:


	"targetPath": "build",
	"mainSourceFile": "alerter.d",
	"sourcePaths": ["source"],
	"copyFiles": ["./resources"],
	"name": "alerter",
	"buildTypes": {
		"release": {
			"buildOptions": [
				"releaseMode",
				"inline",
				"optimize"
			]
		},
		"tests": {
			"buildOptions": [
				"unittests"
			]
		}
	}
}

tastyminerals avatar Aug 11 '21 10:08 tastyminerals

Where is the configurations array ? What I meant was:

	"configurations": [
		{
			"name": "debug",
			"mainSourceFile": "source/alerter.d"
		},
		{
			"name": "unittest"
		}
	]

Geod24 avatar Aug 12 '21 00:08 Geod24

Here is the current dub.json

{
	"name": "alerter",
	"sourcePaths": ["source"],
	"copyFiles": ["./resources"],
	"targetPath": "build",
	"configurations": [
		{
			"name": "debug",
			"mainSourceFile": "source/alerter.d"
		},
		{
			"name": "unittest"
		}
	],
	"buildTypes": {
		"release": {
			"buildOptions": [
				"releaseMode",
				"inline",
				"optimize"
			]
		},
		"tests": {
			"buildOptions": [
				"unittests"
			]
		}
	}
}

And here is what I get, when I try to run dub build in the project dir.

Performing "debug" build using /usr/local/bin/ldc for aarch64, arm_hardfloat.
alerter ~master: building configuration "alerter"...
Error: No source files
/usr/local/bin/ldc failed with exit code 1.

I tried dub -b options and they also don't work.

tastyminerals avatar Aug 12 '21 07:08 tastyminerals

alerter ~master: building configuration "alerter"...

So you aren't building the debug configuration, and source/alerter.d is excluded from the alerter configuration.

Geod24 avatar Aug 12 '21 08:08 Geod24

alerter ~master: building configuration "alerter"...

So you aren't building the debug configuration, and source/alerter.d is excluded from the alerter configuration.

Ok, dub -b debug successfully builds the project. However, it uses the "library" target type by default and if I specify the "targetType" : "executable"

		{
			"name": "debug",
			"mainSourceFile": "alerter.d",
			"targetType": "executable"
		}

I get :(

Performing "debug" build using /usr/local/bin/ldc for aarch64, arm_hardfloat.
alerter ~master: building configuration "debug"...
Error: module `alerter` is in file 'alerter.d' which cannot be read
import path[0] = source/
import path[1] = /Users/pavels/.local/share/ldc2/bin/../import
/usr/local/bin/ldc failed with exit code 1.

Do you have a working dub.json by chance for a single executable script with a custom name?

tastyminerals avatar Aug 12 '21 13:08 tastyminerals

I have run into this problem as well and while searching for answers, I ran into this GitHub issue. In the current dub version, the system does work, but it's quite confusing for newer engineers due to the following reasons:

  1. The implicitly generated application or library configurations are not visible, so there is no easy way to know that special logic was being done by default, and the user just assumes this is normal behavior.
  2. The definition of any configuration causes the defaults to be lost, e.g. someone adding a special test configuration suddenly finds errors like this while simply trying to build the app or run unittests.
  3. Even after the user discovers they must now re-write multiple configurations, the default behavior of the generated 'application' or 'library' configs is not documented, thus the engineer has to figure it out someone through trial and error.

With that being said, I find the following dub.sdl does most of what is needed for my use case (having a separate integration test configuration that tests the fully built program from the outside):

authors "Vijay Nayar"
copyright "Copyright © 2019, Vijay Nayar"
description "Receives data from sources, converts it to protobufs, and delivers it in batches to stem."
license "proprietary"
name "mouth"
targetPath "target"

# By default, 'dub' creates either an 'application' or 'library' config if no configuration exists.
# Because we need a configuration for integration tests, this forces us to create the
# 'application' config as well. 

# Used by 'dub build' and 'dub test', because it is the first 'executable' config present.
configuration "application" {
  targetName "mouth"
  targetType "executable"
  # Your dependencies may vary.
  dependency "funnel:common" path="../"
  dependency "funnel:proto" path="../"
  dependency "nanomsg-wrapper" version="~>0.5.3"
  dependency "poodinis" version="~>8.0.3"
  dependency "vibe-d" version="~>0.9.4"
  # This must be listed so that 'dub test' knows to ignore it.
  mainSourceFile "source/app.d"
}

# Used by 'dub test --config=integration'
configuration "integration" {
  targetName "mouth_integration"
  # This must not be 'library', or it will be used by 'dub test', which first looks for 'library' then 'executable' types.
  targetType "executable"
  # The "source/" directory is automatically included, it must be explicitly excluded instead.
  excludedSourceFiles "source/*"
  # The integration tests' source is in './integration'.
  sourcePaths "integration"
  importPaths "integration"
  # Make sure the executable we are testing exists.
  preBuildCommands "echo IMPORT_PATHS=$$IMPORT_PATHS"
  preRunCommands "cd $PACKAGE_DIR ; dub build"
}

vnayar avatar Feb 06 '22 20:02 vnayar