Skip to content

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

Open

Description

@andre2007

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

Activity

  1. Timoses commented on Dec 20, 2018

    @Timoses

    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?

  2. tastyminerals commented on Aug 11, 2021

    @tastyminerals

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

  3. Geod24 commented on Aug 11, 2021

    @Geod24
    Member

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

  4. tastyminerals commented on Aug 11, 2021

    @tastyminerals

    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"
    			]
    		}
    	}
    }
  5. Geod24 commented on Aug 12, 2021

    @Geod24
    Member

    Where is the configurations array ?
    What I meant was:

    	"configurations": [
    		{
    			"name": "debug",
    			"mainSourceFile": "source/alerter.d"
    		},
    		{
    			"name": "unittest"
    		}
    	]
  6. tastyminerals commented on Aug 12, 2021

    @tastyminerals

    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.

  7. Geod24 commented on Aug 12, 2021

    @Geod24
    Member

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

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

  8. tastyminerals commented on Aug 12, 2021

    @tastyminerals

    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?

  9. vnayar commented on Feb 6, 2022

    @vnayar

    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"
    }
    
  10. added this to the 2.0.0 milestone on Feb 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions