Search Unity

Feedback Feature Request : Allow Packages to define GLOBAL Script Define Symbols

Discussion in 'Package Manager' started by NWalker_FGOL_Ubisoft, Jul 8, 2020.

  1. NWalker_FGOL_Ubisoft

    NWalker_FGOL_Ubisoft

    Joined:
    Aug 10, 2015
    Posts:
    25
    Example problem:
    1. I have a package "FOO" that implements feature "FOO_A".
    2. I have a package "BAR" that implements feature "BAR_B".
    3. I have a small bit of code that depends on both FOO_A and BAR_B, to create feature "FOOBAR_C".

    This raises the following problem:

    Our company has created literally hundreds of FOOBAR_C code snippets. Some are large enough to justify their own package, but many are not.

    But, I would still like to package these smaller FOOBAR_C features. It seems like I have to create a third package, FOOBAR, which depends on the other two. This is cumbersome and leads to exponential package creation. Bleh.

    Thus, the feature request:
    Feature: Simply including a package in a project would add all scripting define symbols defined in the package.json to the project-wide Script Define Symbols List, implicitly. I could then use this global scripting define symbol to conditionally add this FOOBAR_C code.

    Maybe the Inspector could also allow you to TOGGLE those define symbols, with true as the default value.

    Thus, rather than having hundreds of packages, I only need optional dependencies and preprocessor directives (e.g.
    #if
    ), making FOOBAR_C automatically enabled when those two packages merge.

    Adding this new feature to Unity's example (from here), it would look like this:

    Code (JSON):
    1.  
    2. {
    3.   "name": "com.unity.example",
    4.   "version": "1.2.3",
    5.   "displayName": "Package Example",
    6.   "description": "This is an example package",
    7.   "unity": "2019.1",
    8.   "unityRelease": "0b5",
    9.   "dependencies": {
    10.     "com.unity.some-package": "1.0.0",
    11.     "com.unity.other-package": "2.0.0"
    12. },
    13. "keywords": [
    14.     "keyword1",
    15.     "keyword2",
    16.     "keyword3"
    17.   ],
    18.   "author": {
    19.     "name": "Unity",
    20.     "email": "unity@example.com",
    21.     "url": "https://www.unity3d.com"
    22.   },
    23. "scriptDefines": [
    24.     "UNITY_SOMEPACKAGE",
    25.     "SOME_CUSTOM_FEATURE_ENABLED",
    26.   ],
    27. }
    Note the new
    "scriptDefines"
    JSON key with an array of values.

    You could even automatically generate based on the name.
    E.g.
    com.unity.example
    becomes
    COM_UNITY_EXAMPLE
    .

    Obviously, this is all possible manually or via our own custom script, but it would be nice to automate. Unity obviously does this under the hood for some internal tools. Thanks!
     
  2. UnityMaru

    UnityMaru

    Community Engagement Manager PSM

    Joined:
    Mar 16, 2016
    Posts:
    1,227
    Hello there. Thank you for your feedback and apologies for the delay in reply.

    Unfortunately, this would be something that we cannot implement at present. This can/will introduce DEFINE conflicts and problems. For instance, project code could rely on FOOBAR_C to be present to turn on some code, and an unrelated package happens to declare that define, causing the code to be "turned on", but failing to compile because the actual code it needs to use isn't even present.

    The other idea that there could be defines generated automatically, e.g. "COM_UNITY_EXAMPLE", is something we don't see much benefit from: without the package version, most dependent code will be turned on (similarly to the above example) and break if the wrong package version is there. We could generate instead: COM_UNITY_EXAMPLE, COM_UNITY_EXAMPLE_2, COM_UNITY_EXAMPLE_2_OR_NEWER, COM_UNITY_EXAMPLE_2_1, COM_UNITY_EXAMPLE_2_0_OR_NEWER, COM_UNITY_EXAMPLE_2_1_OR_NEWER, COM_UNITY_EXAMPLE_2_1_5, COM_UNITY_EXAMPLE_2_1_0_OR_NEWER, ... but as you can easily see that list wouldn't scale up with hundreds of packages at high versions.

    A potential workaround for your specific use cases would be to accompany your code snippets with .asmdef files, and use the "versionDefines" property of asmdefs to control which extra define is set.
     
  3. NWalker_FGOL_Ubisoft

    NWalker_FGOL_Ubisoft

    Joined:
    Aug 10, 2015
    Posts:
    25
    @UnityMaru Fair points, and no worries about the delay. Yeah - generating defines would get quite large quite quickly.

    But, out of curiosity, is there a max character limit or count on Script Define Symbols? If not, is there a specific concern about bloat? Even a few hundred packages would only result in a few kb of defines, and it actually would then allow very granular package and dependency management.

    AFAIK, preprocessor directives are not included in built .DLL's, so it would only bloat the IDE (possibly) and project (marginally).
     
    imblue4d and noio like this.
  4. imblue4d

    imblue4d

    Joined:
    May 27, 2016
    Posts:
    110
    This is a very important feature!
    Please consider implementing it, at least just for the package name (COM_UNITY_EXAMPLE)
    Even without version it helps activate code that operates on its own to serve another package!
    So 1 define is better than none!
     
  5. Spy-Master

    Spy-Master

    Joined:
    Aug 4, 2022
    Posts:
    620
    As the employee mentioned, version defines can help. I believe they can basically do what you want with nothing special needed.
    https://docs.unity3d.com/Manual/ScriptCompilationAssemblyDefinitionFiles.html#define-symbols
    All you need to do is add the appropriate entries for the asmdef file for the assemblies that require specific packages to be available for parts of the code contained in the assembly. Unity’s first-party packages already do something like this for handling URP/HDRP-specific code when you install one of those render pipelines. This also handles the case where you need to compose a synthetic define based off multiple packages being present - you can do something like the following at the start of C# files that need that symbol:
    Code (csharp):
    1. #if PACKAGE_1_DEFINE && PACKAGE_2_DEFINE
    2. #DEFINE COMBINED_DEFINE_1
    3. #endif
    This does get a little repetitive, but it’s probably the least of your worries - you can get the functionality (more or less) that OP wants when you package code as a custom assembly, which generally should be the case - you should be able to avoid falling back to dumping your code in Assembly-CSharp just by using your own custom project-wide assembly. You gain additional control over the compilation of your code and don’t really lose much in that transition. When doing that, the real pain is making sure all required assembly definition references etc. are added, but that’s only when configuring things at first or when adding a new package with an assembly that will be referenced by code in that assembly.
     
    maximeb_unity likes this.
  6. maximeb_unity

    maximeb_unity

    Unity Technologies

    Joined:
    Mar 20, 2018
    Posts:
    555
    As pointed out by @Spy-Master, using .asmdef assets in your own code provides you with this functionality.

    Assembly definition assets group your scripts to be compiled into a separate assembly. They let you configure optional defines based on the presence of other packages (at specific versions or not) as well as Unity versions (see "Version Defines" linked by @Spy-Master above). They also let you configure assembly-wide conditionals so that all scripts in that assembly can be skipped from compilation based on these scripting symbols (see the other paragraph on this in the same Manual page at https://docs.unity3d.com/Manual/ScriptCompilationAssemblyDefinitionFiles.html#conditional-assembly).

    Note that assembly definitions can be used for scripts under your own project (ie. under Assets), not just for scripts in packages. When using an assembly definition, Editor scripts are no longer detected as such based on their file path, but based on the platforms listed in the assembly definition. In that case, you would need to select "Editor" as the sole platform for that Editor assembly.
     
    Spy-Master and Anthiese like this.
  7. imblue4d

    imblue4d

    Joined:
    May 27, 2016
    Posts:
    110
    Ok thanks for the response, i'm already doing this for my upm packages but the definition is not detectable from outside an asmdef unless there's asmdefs everywhere

    the end user will have to create an asmdef rule for a define for my package,
    and in some cases even an asmdef file, as i'm sure some people don't use them

    My post was about an automatic per-package single define that the user could have without setting up any .asmdef or define rule
    it can be triggered optionally (i.e. toggle in the package's asmdef)

    Also another solution could be to expand the Settings/Player defines section so that the user can add custom rules just as in an asmdef (for users that still don't use them)
     
  8. maximeb_unity

    maximeb_unity

    Unity Technologies

    Joined:
    Mar 20, 2018
    Posts:
    555
    Hi @ANFADEV,

    That was a solution we considered years ago but ended up discarding as it increases the number of defines linearly with the number of packages (or worse, if we also implemented an exponentially-growing list with _X_Y ... _X_Y_OR_NEWER patterns!) and, without any version information, it has limited usefulness, because code APIs in packages evolve over time and are identified by their version; without versioning information, code conditionally/optionally calling those APIs could not be made to be safely wrapped in defines, since the define would be activated regardless of the version (and regardless of the actual API). It certainly works for some use cases where the point is to enable a feature that doesn't directly call those APIs, but it's extremely limited and could not be used safely for API calls. The current system provides full control to the consumer: the provider cannot forcibly add defines to the consumer's project (which is good), instead the consumer (ie project owner, dependent package author...) must decide when and where to use defines and how they should be named, without any risk of side-effects when compiling other consumers' code.

    I understand this feature would have been useful to you in your scenario. Since packages must use asmdef and you're concerned about the cases where there are no asmdefs, then you could consider using the PlayerSettings.SetScriptingDefineSymbols API from your package so that it configures something when it is installed, if you don't mind altering your users' project settings forcibly like this (perhaps guarded by a confirmation dialog or something to make this clear and opt-in).
     
  9. imblue4d

    imblue4d

    Joined:
    May 27, 2016
    Posts:
    110
    Thanks for the detailed response, and for shedding some light further on the motivations behind your decisions.
    This clears all remaining uncertainty about how to proceed in the best way (from a user perspective).
     
  10. maximeb_unity

    maximeb_unity

    Unity Technologies

    Joined:
    Mar 20, 2018
    Posts:
    555
    If we're discussing whether a user could enable scripting symbols without converting their project entirely to asmdefs, I could suggest using the aforementioned PlayerSettings.SetScriptingDefineSymbols in a script in a dedicated Editor assembly with its own .asmdef that uses both VersionDefine and DefineConstraints, so that small utility assembly is compiled and loaded conditionally to the presence of a specific package (with or without a specific version range defined). That code could use
    PlayerSettings.GetScriptingDefineSymbols
    to check if the define is set (or otherwise track somehow whether it needs to run or not), and
    PlayerSettings.SetScriptingDefineSymbols
    to add the define.

    Granted, this would be much easier if the notion of "Version Define" was just simply present in the Project Settings. Maybe this would be a desirable feature to request from the scripting pipeline. If you want to go ahead with this suggestion or your initial idea, you can submit a feature request here which will allow your idea to be tracked in our systems (look for the "Submit a new idea!" tile in the table, you'll need to scroll down). Forums are not used to track feature requests.
     
    Last edited: Mar 13, 2023
    imblue4d likes this.