Keywords: UE4, Building Multiple Plugins, Dependencies, Without Source, Source Removed, Prebuilding, Precompile, Distributing

GUI

1, New two plugins (Edit -> Plugins -> New Plugin) that named as P1 and P2.

2, Add source for P1 and P2.

We have assumed that Actor1 was in plugin P1, and Actor2 was in plugin P2, P2 has dependencies on P1, but P1 has no dependencies on P2.

Actor1.h

#include "Actor1.h"
#include "Engine.h"

// Sets default values
AActor1::AActor1()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AActor1::BeginPlay()
{
    Super::BeginPlay();
    
    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, FString("Actor1 BeginPlay"));
}

Actor2.h

#include "Actor2.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "Actor1.h"

// Sets default values
AActor2::AActor2()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AActor2::BeginPlay()
{
    Super::BeginPlay();
    
    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString("Actor2 BeginPlay"));

    FTransform Trans;
    AActor1 A1 = GetWorld()->SpawnActor<AActor1>(AActor1::StaticClass(), Trans);
}

3, List P1 in PrivateDependencyModuleNames of Build.cs of P2:

PrivateDependencyModuleNames.AddRange(
    new string[]
    {
        "CoreUObject",
        "Engine",
        "Slate",
        "SlateCore",
        "P1"
        // ... add private dependencies that you statically link with here ...	
    }
    );

4, List plugin P1 in P2.uplugin, then add WhitelistPlatforms in P1.uplugin and P2.uplugin:

P1.uplugin

{
    "FileVersion": 3,
    "Version": 1,
    "VersionName": "1.0",
    "FriendlyName": "P1",
    "Description": "",
    "Category": "Other",
    "CreatedBy": "",
    "CreatedByURL": "",
    "DocsURL": "",
    "MarketplaceURL": "",
    "SupportURL": "",
    "CanContainContent": true,
    "IsBetaVersion": false,
    "IsExperimentalVersion": false,
    "Installed": false,
    "Modules": [
        {
            "Name": "P1",
            "Type": "Runtime",
            "LoadingPhase": "Default",
            "WhitelistPlatforms": [
                "Win64"
            ]
        }
    ]
}

P2.uplugin

{
    "FileVersion": 3,
    "Version": 1,
    "VersionName": "1.0",
    "FriendlyName": "P2",
    "Description": "",
    "Category": "Other",
    "CreatedBy": "",
    "CreatedByURL": "",
    "DocsURL": "",
    "MarketplaceURL": "",
    "SupportURL": "",
    "CanContainContent": true,
    "IsBetaVersion": false,
    "IsExperimentalVersion": false,
    "Installed": false,
    "Modules": [
        {
            "Name": "P2",
            "Type": "Runtime",
            "LoadingPhase": "Default",
            "WhitelistPlatforms": [
                "Win64"
            ]
        }
    ],
    "Plugins": [
        {
            "Name": "P1",
            "Enabled": true
        }
    ]
}

5, Close editor, rebuild project source and start editor.

6, Package plugin P1: Edit -> Plugins -> Project -> P1 -> Package

then close editor and remove TestProj\Plugins\P1, then copy packaged version of P1 into engine plugins directory.

then you can remove private source files:

When restart editor, you will see the packaged plugin P1 in Installed tab of plugins.

7, Package plugin P2: Edit -> Plugins -> Project -> P2 -> Package:

then close editor and remove TestProj\Plugins\P2, then copy packaged version of P2 into engine plugins directory.

then you can remove private source files:

8, List P2 in PublicDependencyModuleNames of TestProj.Build.cs:

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class TestProj : ModuleRules
{
    public TestProj(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "NavigationSystem", "AIModule" });

        PublicDependencyModuleNames.AddRange(new string[] { "P2" });
    }
}

9, List P1 and P2 in TestProj.uproject

{
    "FileVersion": 3,
    "EngineAssociation": "4.24",
    "Category": "",
    "Description": "",
    "Modules": [
        {
            "Name": "TestProj",
            "Type": "Runtime",
            "LoadingPhase": "Default"
        }
    ],
    "Plugins": [
        {
            "Name": "P1",
            "Enabled": true
        },
        {
            "Name": "P2",
            "Enabled": true
        }
    ]
}

Otherwise you would get error at building:

The game module `TestProj` could not be loaded. There may be an operating system error or the module may not be properly set up.

9, Now you can inlcude plugin P2 headers in your game project:

#include "TestProjGameMode.h"
#include "TestProjPlayerController.h"
#include "TestProjCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include "Actor2.h"

ATestProjGameMode::ATestProjGameMode()
{
    // use our custom PlayerController class
    PlayerControllerClass = ATestProjPlayerController::StaticClass();

    // set default pawn class to our Blueprinted character
    static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/TopDownCPP/Blueprints/TopDownCharacter"));
    if (PlayerPawnBPClass.Class != NULL)
    {
        DefaultPawnClass = PlayerPawnBPClass.Class;
    }
}

void ATestProjGameMode::StartPlay()
{
    Super::StartPlay();

    FTransform Trans;
    AActor2* Pawn = GetWorld()->SpawnActor<AActor2>(AActor2::StaticClass(), Trans);
}

then rebuild project source and start editor, now you can package project:
File -> Package Project -> Windows -> Win64.

CLI

Command:

"D:\Epic Games\UE_4.24\Engine\Binaries\DotNET\AutomationTool.exe" BuildPlugin -Plugin=E:/TestProj/Plugins/MyPlugin/MyPlugin.uplugin -Package=F:/Build/MyPlugin -CreateSubFolder

Source file of BuildPlugin command :

Engine\Source\Programs\AutomationTool\Scripts\BuildPluginCommand.Automation.cs

If a man is to shed the light of the sun upon other men, he must first of all have it within himself. ― Romain Rolland