That's a good point, I'm sure the solution to this problem can be generalised to be more useful. The key problem, as you've said, is what we should do if the user tries to build a file that is the target for multiple tasks; the problem is ambiguity.
The first, obvious suggestion, is for the user to disambiguate the tasks in their code, so doit knows from the beginning which task should run. For example, the user could provide a priority for each target, indicating which task should be run if there is a conflict:
def task_a():
return {
'targets': [{'path': 'file.txt', 'priority': 1}]
}
def task_b():
return {
'targets': [{'path': 'file.txt', 'priority': 2}]
}
This idea could be expanded into the user giving a priority function etc.
However, what I think is a better solution, although it would be a more drastic change, is for the 'multiple tasks have the same target' error to only occur at execution time, not while doit is performing its first pass to analyse the tasks. This means, in my use case, that `doit install` would have no issue, as I've designed, because in one task generator it checks to see if the user has the right credentials, and if so, sets the task_dep to 'download_asset_bundle', whereas if they don't, it sets it to 'download_assets_manually'. In the case that the user should write `doit asset.tar.gz`, i.e. they ask for a specific target to be built, doit should then throw an exception, with the message 'ambigious build task for target asset.tar.gz: please specify either the task "download_assets_manually" or "download_asset_bundle"'.
The problem with this solution is that errors by the user where they accidentally set two tasks to have the same target will be harder to catch, for example if they copy a task and forget to edit the target, they made a mistake and they should be told. So perhaps the solution is to make doit complain about ambigious targets at load time, unless the user sets a configuration option to complain at runtime?