override fun resolveProjectInfo()

in platformio/src/com/jetbrains/cidr/cpp/embedded/platformio/project/PlatformioProjectResolver.kt [84:245]


  override fun resolveProjectInfo(id: ExternalSystemTaskId,
                                  projectPath: String,
                                  isPreviewMode: Boolean,
                                  settings: PlatformioExecutionSettings?,
                                  resolverPolicy: ProjectResolverPolicy?,
                                  listener: ExternalSystemTaskNotificationListener): DataNode<ProjectData>? {
    cancelled = false
    val project = id.findProject()!!
    val platformioService = project.service<PlatformioService>()

    if (!TrustedProjects.isProjectTrusted(project)) {
      // To prevent a deadlock
      assert(!EDT.isCurrentThreadEdt())
      runBlockingCancellable {
        if (!showUntrustedProjectLoadDialog(project)) {
          platformioService.projectStatus = NOT_TRUSTED
          throw ExternalSystemException(ClionEmbeddedPlatformioBundle.message("project.not.trusted"))
        }
      }
    }

    platformioService.projectStatus = PARSING
    try {
      val projectFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(projectPath) ?: throw ExternalSystemException(FileNotFoundException(ClionEmbeddedPlatformioBundle.message("project.not.found", projectPath)))
      val projectDir = if (projectFile.isDirectory) projectFile else projectFile.parent
      val boardInfo = project.getUserData(PROJECT_INIT_KEY)
      if (boardInfo != null) {
        cliGenerateProject(project, listener, id, projectDir, boardInfo)
      }
      checkCancelled()
      val pioResolvePolicy = resolverPolicy.asSafely<PlatformioProjectResolvePolicy>()
      if (pioResolvePolicy?.cleanCache != false) {
        platformioService.cleanCache()
      }

      checkCancelled()
      var configJson = platformioService.configJson
      if (configJson == null) {
        configJson = gatherConfigJson(id, "pio-run:${UUID.randomUUID()}", project, listener)
        platformioService.configJson = configJson
      }

      val envs: Map<String, PlatformioExecutionTarget>
      val defaultEnv: PlatformioExecutionTarget?
      checkCancelled()
      val configMap: Map<String, List<Any>> =
        @Suppress("UNCHECKED_CAST")
        (Gson().fromJson(configJson, List::class.java) as List<List<Any>>)
          .associate { it[0] as String to it[1] as List<Any> }
      val platformioSection: Map<String, Any> =
        @Suppress("UNCHECKED_CAST")
        (configMap["platformio"] as List<List<Any>>?)?.associate { it[0] as String to it[1] } ?: emptyMap()

      envs = configMap.keys
        .filter { it.startsWith("env:") }
        .associate {
          val envName = it.removePrefix("env:")
          envName to PlatformioExecutionTarget(envName)
        }
      when (val defaultEnvs = platformioSection["default_envs"]) {
        is List<*> -> defaultEnv = envs[defaultEnvs[0].asSafely<String>() ?: ""]
        is String -> defaultEnv = envs[defaultEnvs]
        else -> defaultEnv = envs.values.firstOrNull()
      }
      val scanner = PlatformioFileScanner(projectDir, listener, id, this::checkCancelled)
      platformioService.iniFiles = scanner.findConfigs(platformioSection)

      val projectName = platformioSection["name"]?.asSafely<String>() ?: project.name
      val ideProjectPath = ExternalSystemApiUtil.toCanonicalPath(project.basePath ?: projectDir.path)
      val projectData = ProjectData(ID, projectName, ideProjectPath,
                                    ExternalSystemApiUtil.toCanonicalPath(projectDir.path))

      var projectNode: DataNode<ProjectData>
      do {
        val activeExecutionTarget = ExecutionTargetManager.getActiveTarget(project)
        var activeEnv = activeExecutionTarget as? PlatformioExecutionTarget
        platformioService.envs = envs.values.toList()
        createRunConfigurationIfRequired(project)
        if (activeEnv == null || !envs.containsKey(activeEnv.id)) {
          activeEnv = defaultEnv
          ApplicationManager.getApplication().invokeAndWait {
            ExecutionTargetManager.setActiveTarget(project, activeEnv ?: DefaultExecutionTarget.INSTANCE)
          }
        }
        val activeEnvName = ExecutionTargetManager.getActiveTarget(project).asSafely<PlatformioExecutionTarget>()?.id ?: defaultEnv?.id
        if (activeEnvName == null) throw ExternalSystemException("No active platformio environment")

        var pioActiveMetadataText = platformioService.metadataJson[activeEnvName]
        if (pioActiveMetadataText == null) {
          pioActiveMetadataText = gatherEnvMetadata(id, "pio-run:${UUID.randomUUID()}", project, activeEnvName, listener)
          platformioService.setMetadataJson(activeEnvName, pioActiveMetadataText)
        }
        val pioActiveMetadata = Gson()
                                  .fromJson<Map<String, Any>>(pioActiveMetadataText, Map::class.java)
                                  ?.get(activeEnvName)
                                  ?.asSafely<Map<String, Any>>()
                                ?: throw ExternalSystemException("Project metadata does not contain expected '${activeEnv}' section")
        platformioService.targetExecutablePath = pioActiveMetadata["prog_path"] as? String
        platformioService.svdPath = pioActiveMetadata["svd_path"] as? String
        val targets = (pioActiveMetadata["targets"]?.asSafely<List<Map<String, String?>>>())?.map {
          val name = it["name"] ?: throw ExternalSystemException("Malformed metadata targets section")
          PlatformioTargetData(name, it["title"], it["description"], it["group"])
        }
        platformioService.setTargets(addUploadIfMissing(targets.orEmpty()))

        val buildDirectory: Path = calcBuildDir(projectDir, platformioSection)
        platformioService.buildDirectory = buildDirectory
        val name = pioActiveMetadata["env_name"] as String
        val confBuilder = ExternalResolveConfigurationBuilder(id = name, configName = "PlatformIO", buildWorkingDir = projectDir.toNioPath().toFile())
          .withVariants(name)

        checkCancelled()

        val languageConfigurations = configureLanguages(pioActiveMetadata, confBuilder)

        checkCancelled()

        val compDbJson = getCompDbJson(pioResolvePolicy, platformioService, id, project, activeEnvName, listener, projectPath)

        checkCancelled()
        scanner.scanSources(compDbJson, project.service<PlatformioWorkspace>(), languageConfigurations, confBuilder)
        checkCancelled()

        platformioService.librariesPaths = scanner.scanLibraries(pioActiveMetadata)

        checkCancelled()
        val module = ExternalModuleImpl(mutableSetOf(confBuilder.invoke()))
        val cppModuleNode = DataNode(ExternalModule.OC_MODULE_KEY, module, null)
        projectNode = DataNode(ProjectKeys.PROJECT, projectData, null)
        attachExternalModule(project, ID, projectDir.toNioPath(), projectNode, cppModuleNode)
        val needsToBeReparsed = with(ExecutionTargetManager.getActiveTarget(project)) {
          this is PlatformioExecutionTarget && this.id != activeEnvName
        }
      }
      while (needsToBeReparsed)
      platformioService.projectStatus = PARSED
      project.messageBus.syncPublisher(PLATFORMIO_UPDATES_TOPIC).projectStateChanged()
      return projectNode
    }
    catch (e: ProcessCanceledException) {
      platformioService.projectStatus = PARSE_FAILED
      throw e
    }
    catch (e: RunPlatformioException) {
      platformioService.projectStatus = when(e) {
        is RunPlatformioExitcodeException -> PARSE_FAILED
        is RunPlatformioExecutionException -> UTILITY_FAILED
        else -> PARSE_FAILED
      }
      LOG.info(e)
      return null
    }
    catch (e: ExternalSystemException) {
      platformioService.projectStatus = PARSE_FAILED
      throw e
    }
    catch (e: Throwable) {
      platformioService.projectStatus = PARSE_FAILED
      LOG.error(e)
      throw ExternalSystemException(e)
    }
  }