github.com/tiagovtristao/plz@v13.4.0+incompatible/src/ide/intellij/module.go (about)

     1  package intellij
     2  
     3  import (
     4  	"encoding/xml"
     5  	"fmt"
     6  	"io"
     7  	"path/filepath"
     8  
     9  	"github.com/thought-machine/please/src/core"
    10  )
    11  
    12  // Module represents the IntelliJ concept of a module
    13  type Module struct {
    14  	XMLName    xml.Name          `xml:"module"`
    15  	ModuleType string            `xml:"type,attr"`
    16  	Version    int               `xml:"version,attr"`
    17  	Component  ModuleComponent   `xml:"component"`
    18  }
    19  
    20  func newModule(graph *core.BuildGraph, target *core.BuildTarget) Module {
    21  	component := newModuleComponent(target)
    22  	component.addOrderEntry(newSourceFolderEntry(false))
    23  
    24  	for _, label := range target.DeclaredDependencies() {
    25  		dep := graph.TargetOrDie(label)
    26  		if shouldMakeModule(dep) {
    27  			component.addOrderEntry(newModuleEntry(moduleName(label)))
    28  		}
    29  	}
    30  
    31  	module := Module{
    32  		ModuleType: "WEB_MODULE",
    33  		Version:    4,
    34  		Component: component,
    35  	}
    36  
    37  	return module
    38  }
    39  
    40  func newJavaModule(graph *core.BuildGraph, target *core.BuildTarget) Module {
    41  	module := newModule(graph, target)
    42  	module.ModuleType = "JAVA_MODULE"
    43  	module.Component.addOrderEntry(newInheritedJdkEntry())
    44  
    45  	if shouldMakeLibrary(target) {
    46  		module.Component.addOrderEntry(newLibraryEntry(libraryName(target), "project"))
    47  	}
    48  	return module
    49  }
    50  
    51  func newScalaModule(graph *core.BuildGraph, target *core.BuildTarget) Module {
    52  	module := newJavaModule(graph, target)
    53  
    54  	module.Component.addOrderEntry(newLibraryEntry("scala-sdk", "application"))
    55  
    56  	return module
    57  }
    58  
    59  func shouldMakeJavaModule(target *core.BuildTarget) bool {
    60  	for _, label := range target.PrefixedLabels("rule:") {
    61  		if label == "java_library" {
    62  			return true
    63  		} else if label == "java_test" {
    64  			return true
    65  		} else if label == "maven_jar" {
    66  			return true
    67  		}
    68  	}
    69  	return false
    70  }
    71  
    72  func shouldMakeScalaModule(target *core.BuildTarget) bool {
    73  	for _, label := range target.PrefixedLabels("rule:") {
    74  		if label == "scala_library" {
    75  			return true
    76  		}
    77  	}
    78  	return false
    79  }
    80  
    81  func shouldMakeStaticModule(target *core.BuildTarget) bool {
    82  	for _, label := range target.PrefixedLabels("rule:") {
    83  		if label == "resources" || label == "test_resources" {
    84  			return true
    85  		}
    86  	}
    87  	return false
    88  }
    89  
    90  func shouldMakeModule(target *core.BuildTarget) bool {
    91  	return shouldMakeStaticModule(target) ||
    92  		shouldMakeJavaModule(target) ||
    93  		shouldMakeScalaModule(target)
    94  }
    95  
    96  func shouldHaveContent(target *core.BuildTarget) bool {
    97  	for _, label := range target.PrefixedLabels("rule:") {
    98  		if label == "java_library" {
    99  			return true
   100  		} else if label == "java_test" {
   101  			return true
   102  		} else if label == "scala_library" {
   103  			return true
   104  		} else if label == "resources" {
   105  			return true
   106  		} else if label == "test_resources" {
   107  			return true
   108  		}
   109  	}
   110  	return false
   111  }
   112  
   113  func shouldMakeLibrary(target *core.BuildTarget) bool {
   114  	for _, label := range target.PrefixedLabels("rule:") {
   115  		if label == "maven_jar" {
   116  			return true
   117  		}
   118  	}
   119  	return false
   120  }
   121  
   122  func isTestModule(target *core.BuildTarget) bool {
   123  	if target.IsTest {
   124  		return true
   125  	}
   126  	for _, label := range target.PrefixedLabels("rule:") {
   127  		if label == "java_test" {
   128  			return true
   129  		}
   130  	}
   131  	return false
   132  }
   133  
   134  func (module *Module) toXML(writer io.Writer) {
   135  	writer.Write([]byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"))
   136  
   137  	content, err := xml.MarshalIndent(module, "", "  ")
   138  	if err  == nil {
   139  		writer.Write(content)
   140  	}
   141  }
   142  
   143  // ModuleComponent represents the top level module wrapper.
   144  type ModuleComponent struct {
   145  	XMLName               xml.Name       `xml:"component"`
   146  	Name                  string         `xml:"name,attr"`
   147  	InheritCompilerOutput bool           `xml:"inherit-compiler-output,attr"`
   148  	Content               *ModuleContent `xml:"content,omitEmpty"`
   149  	OrderEntries          []OrderEntry   `xml:"orderEntry"`
   150  }
   151  
   152  func newModuleComponent(target *core.BuildTarget) ModuleComponent {
   153  	orderEntries := []OrderEntry{}
   154  
   155  	return ModuleComponent{
   156  		Name: "NewModuleRootManager",
   157  		InheritCompilerOutput: true,
   158  		Content:               newModuleContent(target),
   159  		OrderEntries:          orderEntries,
   160  	}
   161  }
   162  
   163  func (moduleComponent *ModuleComponent) addOrderEntry(entry OrderEntry) {
   164  	moduleComponent.OrderEntries = append(moduleComponent.OrderEntries, entry)
   165  }
   166  
   167  // ModuleContent is a wrapper that is generally only used once in a given module.
   168  type ModuleContent struct {
   169  	XMLName      xml.Name       `xml:"content"`
   170  	URL          string         `xml:"url,attr"`
   171  	SourceFolder []SourceFolder `xml:"sourceFolder"`
   172  }
   173  
   174  func newModuleContent(target *core.BuildTarget) *ModuleContent {
   175  	if !shouldHaveContent(target) {
   176  		return nil
   177  	}
   178  
   179  	sourceFolders := []SourceFolder{}
   180  	commonDir := target.Label.PackageDir()
   181  
   182  	joined := filepath.Join(core.RepoRoot, commonDir)
   183  	location := relativisedPathTo(moduleDirLocation(target), &joined)
   184  
   185  	sourceFolders = append(sourceFolders, SourceFolder{
   186  		URL:           fmt.Sprintf("file://$MODULE_DIR$/%s", *location),
   187  		IsTestSource:  isTestModule(target),
   188  		PackagePrefix: packagePrefixFromLabels(target.PrefixedLabels("package_prefix:")),
   189  	})
   190  
   191  	packageDir := commonDir
   192  	maybeSrcDir := target.PrefixedLabels("src_dir:")
   193  	if len(maybeSrcDir) == 1 {
   194  		packageDir = filepath.Join(packageDir, maybeSrcDir[0])
   195  	}
   196  	joined = filepath.Join(core.RepoRoot, packageDir)
   197  	contentDir := relativisedPathTo(moduleDirLocation(target), &joined)
   198  
   199  	return &ModuleContent{
   200  		URL:          fmt.Sprintf("file://$MODULE_DIR$/%s", *contentDir),
   201  		SourceFolder: sourceFolders,
   202  	}
   203  }
   204  
   205  // SourceFolder is a location on disk that contains files of interest to this package.
   206  type SourceFolder struct {
   207  	XMLName       xml.Name `xml:"sourceFolder"`
   208  	URL           string   `xml:"url,attr"`
   209  	IsTestSource  bool     `xml:"isTestSource,attr"`
   210  	PackagePrefix *string  `xml:"packagePrefix,attr,omitEmpty"`
   211  }
   212  
   213  // OrderEntry represents a dependency on (e.g.) another module, or a library or an SDK.
   214  type OrderEntry struct {
   215  	XMLName xml.Name `xml:"orderEntry"`
   216  	Type    string   `xml:"type,attr"`
   217  
   218  	ForTests *bool `xml:"forTests,attr,omitEmpty"`
   219  
   220  	Exported *string `xml:"exported,attr,omitEmpty"`
   221  
   222  	LibraryName  *string `xml:"name,attr,omitEmpty"`
   223  	LibraryLevel *string `xml:"level,attr,omitEmpty"`
   224  
   225  	ModuleName *string `xml:"module-name,attr,omitEmpty"`
   226  }
   227  
   228  func newLibraryEntry(name, level string) OrderEntry {
   229  	exported := ""
   230  	return OrderEntry{
   231  		Type:         "library",
   232  		LibraryName:  &name,
   233  		LibraryLevel: &level,
   234  		Exported: &exported,
   235  	}
   236  }
   237  
   238  func newModuleEntry(name string) OrderEntry {
   239  	exported := ""
   240  	return OrderEntry{
   241  		Type:       "module",
   242  		ModuleName: &name,
   243  		Exported: &exported,
   244  	}
   245  }
   246  
   247  func newInheritedJdkEntry() OrderEntry {
   248  	return OrderEntry{
   249  		Type: "inheritedJdk",
   250  	}
   251  }
   252  
   253  func newSourceFolderEntry(forTests bool) OrderEntry {
   254  	return OrderEntry{
   255  		Type:     "sourceFolder",
   256  		ForTests: &forTests,
   257  	}
   258  }
   259  
   260  func toModule(graph *core.BuildGraph, buildTarget *core.BuildTarget) *Module {
   261  	var module *Module
   262  
   263  	if shouldMakeStaticModule(buildTarget) {
   264  		madeModule := newModule(graph, buildTarget)
   265  		module = &madeModule
   266  	}
   267  
   268  	if shouldMakeJavaModule(buildTarget) {
   269  		madeModule := newJavaModule(graph, buildTarget)
   270  		module = &madeModule
   271  	}
   272  
   273  	if shouldMakeScalaModule(buildTarget) {
   274  		madeModule := newScalaModule(graph, buildTarget)
   275  		module = &madeModule
   276  	}
   277  
   278  	return module
   279  }
   280  
   281  func relativisedPathTo(location string, commonPath *string) *string {
   282  	if commonPath == nil {
   283  		return nil
   284  	}
   285  
   286  	rel, err := filepath.Rel(location, *commonPath)
   287  	if err != nil {
   288  		return nil
   289  	}
   290  
   291  	return &rel
   292  }
   293  
   294  func packagePrefixFromLabels(labels []string) *string {
   295  	if len(labels) != 1 {
   296  		return nil
   297  	}
   298  	return &labels[0]
   299  }