Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,10 @@ public class PDEUIMessages extends NLS {

public static String OrganizeManifestsWizardPage_calculateUses;

public static String OrganizeManifestsWizardPage_updateHeader;

public static String OrganizeManifestsOperation_updateHeader;

public static String OverviewPage_contentDescription;

public static String OverviewPage_contentTitle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2381,6 +2381,8 @@ OrganizeManifestsWizardPage_exportedGroup=Exported Packages
OrganizeManifestsWizardPage_markInternal=Mark as &internal all packages that match the following filter:
OrganizeManifestsWizardPage_packageFilter=Package fil&ter:
OrganizeManifestsWizardPage_calculateUses=Calculate 'uses' directive for public packages (this may be a long-running operation)
OrganizeManifestsWizardPage_updateHeader=Update Header from Source Annotations
OrganizeManifestsOperation_updateHeader=updating header for missing required capabilities from source annotations... {0}
OrganizeManifestsWizardPage_markOptional=&marking them as optional
OrganizeManifestsWizardPage_removeUnused=Remo&ve unused dependencies (this may be a long-running operation)
OrganizeManifestsWizardPage_generalGroup=General Manifest Cleanup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public interface IOrganizeManifestsSettings {
public static final String VALUE_DEFAULT_FILTER = "*.internal*"; //$NON-NLS-1$
public static final String PROP_REMOVE_UNRESOLVED_EX = "OrganizeManifests.ExportedPackages.removeUnresolved"; //$NON-NLS-1$
public static final String PROP_CALCULATE_USES = "OrganizeManifests.calculateUses"; //$NON-NLS-1$
public static final String PROP_UPDATE_HEADER = "OrganizeManifests.updateHeader"; //$NON-NLS-1$
public static final String PROP_MODIFY_DEP = "OrganizeManifests.RequireImport.modifyDep"; //$NON-NLS-1$
public static final String PROP_RESOLVE_IMP_MARK_OPT = "OrganizeManifests.RequireImport.resolve:markOptional"; //$NON-NLS-1$
public static final String PROP_UNUSED_DEPENDENCIES = "OrganizeManifests.RequireImport.findRemoveUnused"; //$NON-NLS-1$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ public class OrganizeManifestsProcessor extends RefactoringProcessor implements
// export-package
protected boolean fCalculateUses = false; // calculate the 'uses' directive
// for exported packages

protected boolean fUpdateHeader = false; // automatically adds missing
// required
// capabilities

protected boolean fModifyDep = true; // modify import-package /
// require-bundle
protected boolean fRemoveDependencies = true; // if true: remove, else mark
Expand Down Expand Up @@ -210,6 +215,14 @@ private void runCleanup(IProgressMonitor monitor, IBundlePluginModelBase modelBa
}
}

if (fUpdateHeader) {
subMonitor.subTask(NLS.bind(PDEUIMessages.OrganizeManifestsOperation_updateHeader, projectName));
if (!subMonitor.isCanceled()) {
UpdateHeaderFromAnnotationsOperation.updateHeadersFromAnnotations(fCurrentProject, currentBundle);
subMonitor.worked(3);
}
}

if (fAddDependencies) {
subMonitor.subTask(NLS.bind(PDEUIMessages.OrganizeManifestsOperation_additionalDeps, projectName));
if (!subMonitor.isCanceled()) {
Expand Down Expand Up @@ -316,6 +329,10 @@ public void setCalculateUses(boolean calculateUses) {
fCalculateUses = calculateUses;
}

public void setUpdateHeader(boolean updateHeader) {
fUpdateHeader = updateHeader;
}

public void setModifyDep(boolean modifyDep) {
fModifyDep = modifyDep;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class OrganizeManifestsWizardPage extends UserInputWizardPage implements

private Button fRemoveUnresolved;
private Button fCalculateUses;
private Button fUpdateHeader;
private Button fAddMissing;
private Button fMarkInternal;
private Text fPackageFilter;
Expand Down Expand Up @@ -164,6 +165,13 @@ private void createExportedPackagesGroup(Composite container) {
gd = new GridData();
gd.verticalIndent = 5;
fCalculateUses.setLayoutData(gd);

// issue-1816
fUpdateHeader = new Button(group, SWT.CHECK);
fUpdateHeader.setText(PDEUIMessages.OrganizeManifestsWizardPage_updateHeader);
gd = new GridData();
gd.verticalIndent = 5;
fUpdateHeader.setLayoutData(gd);
}

private void createRequireImportGroup(Composite container) {
Expand Down Expand Up @@ -241,6 +249,10 @@ private void presetOptions() {
fCalculateUses.setSelection(selection);
fProcessor.setCalculateUses(selection);

selection = settings.getBoolean(PROP_UPDATE_HEADER);
fUpdateHeader.setSelection(selection);
fProcessor.setUpdateHeader(selection);

selection = !settings.getBoolean(PROP_MODIFY_DEP);
fModifyDependencies.setSelection(selection);
fProcessor.setModifyDep(selection);
Expand Down Expand Up @@ -290,6 +302,7 @@ protected void performOk() {
settings.put(PROP_INTERAL_PACKAGE_FILTER, fPackageFilter.getText());
settings.put(PROP_REMOVE_UNRESOLVED_EX, !fRemoveUnresolved.getSelection());
settings.put(PROP_CALCULATE_USES, fCalculateUses.getSelection());
settings.put(PROP_UPDATE_HEADER, fUpdateHeader.getSelection());

settings.put(PROP_MODIFY_DEP, !fModifyDependencies.getSelection());
settings.put(PROP_RESOLVE_IMP_MARK_OPT, fOptionalImport.getSelection());
Expand Down Expand Up @@ -318,7 +331,7 @@ private void setEnabledStates() {
private void setButtonArrays() {
fTopLevelButtons = new Button[] { fRemoveUnresolved, fAddMissing, fModifyDependencies, fMarkInternal,
fUnusedDependencies, fAdditonalDependencies, fComputeImportPackages, fFixIconNLSPaths,
fRemovedUnusedKeys, fRemoveLazy, fRemoveUselessFiles, fCalculateUses };
fRemovedUnusedKeys, fRemoveLazy, fRemoveUselessFiles, fCalculateUses, fUpdateHeader };
}

private void setPageComplete() {
Expand Down Expand Up @@ -360,6 +373,8 @@ private void doProcessorSetting(Object source) {
fProcessor.setRemoveUnresolved(fRemoveUnresolved.getSelection());
} else if (fCalculateUses.equals(source)) {
fProcessor.setCalculateUses(fCalculateUses.getSelection());
} else if (fUpdateHeader.equals(source)) {
fProcessor.setUpdateHeader(fUpdateHeader.getSelection());
} else if (fModifyDependencies.equals(source)) {
fProcessor.setModifyDep(fModifyDependencies.getSelection());
} else if (fOptionalImport.equals(source)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*******************************************************************************
* Copyright (c) 2025 Keith Bui and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Keith Bui <[email protected]> - Issue #1816
*******************************************************************************/

package org.eclipse.pde.internal.ui.wizards.tools;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.stream.Collectors;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
//import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.pde.internal.core.ibundle.IBundle;
import org.osgi.framework.Constants;
import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
import org.osgi.resource.Capability;

import aQute.bnd.header.OSGiHeader;
import aQute.bnd.header.Parameters;
import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Jar;
import aQute.bnd.osgi.resource.CapReqBuilder;

/*
* Utility to enhance bundle MANIFEST.MF headers from source annotations,
* similar to Tycho's deriveHeaderFromSource feature. Currently, only Require-Capability
* items are processed.
*/
public class UpdateHeaderFromAnnotationsOperation {

public static void updateHeadersFromAnnotations(IProject project, IBundle bundle) {
IJavaProject javaProject = JavaCore.create(project);
if (javaProject == null || !javaProject.exists()) {
return;
}

try {
IPath outputLocation = javaProject.getOutputLocation();
IPath fullPath = project.getWorkspace().getRoot().getFolder(outputLocation).getLocation();
File outputDir = fullPath.toFile();
if (!outputDir.exists()) {
return;
}

File manifestFile = new File(project.getLocation().toFile(), "META-INF/MANIFEST.MF");

Check warning on line 68 in ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/UpdateHeaderFromAnnotationsOperation.java

View check run for this annotation

Jenkins - eclipse-pde / Compiler

NLS

NORMAL: $ Non-externalized string literal; it should be followed by //$NON-NLS-
Manifest manifest = new Manifest();

if (manifestFile.exists()) {
try (var is = new java.io.FileInputStream(manifestFile)) {
manifest = new Manifest(is);
}
}

// Copy PDE bundle headers into working manifest (to sync PDE model
// state)
for (Object key : bundle.getManifestHeaders().keySet()) {
manifest.getMainAttributes().putValue(
key.toString(),
bundle.getManifestHeaders().get(key).toString()
);
}

try (Jar jar = new Jar(project.getName(), outputDir);
Analyzer analyzer = new Analyzer(jar)) {
// Add project classpath
for (IClasspathEntry entry : javaProject.getResolvedClasspath(true)) {
File f = entry.getPath().toFile();
if (f.exists() && f.length() > 0) {
try {
analyzer.addClasspath(f);
} catch (IOException e) {
// ignore bad entries
}
}
}

Manifest calcManifest = analyzer.calcManifest();
String reqCap = calcManifest.getMainAttributes().getValue(Constants.REQUIRE_CAPABILITY);

Check warning on line 101 in ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/UpdateHeaderFromAnnotationsOperation.java

View check run for this annotation

Jenkins - eclipse-pde / Compiler

Unnecessary Code

NORMAL: The value of the local variable reqCap is not used
mergeRequireCapability(manifest.getMainAttributes(), calcManifest.getMainAttributes());
}
IFile manifestIFile = project.getFile("META-INF/MANFIEST.MF");

Check warning on line 104 in ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/UpdateHeaderFromAnnotationsOperation.java

View check run for this annotation

Jenkins - eclipse-pde / Compiler

NLS

NORMAL: $ Non-externalized string literal; it should be followed by //$NON-NLS-
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
manifest.write(baos);
byte[] bytes = baos.toByteArray();
if (manifestIFile.exists()) {
manifestIFile.setContents(new ByteArrayInputStream(bytes), true, false, null);
} else {
manifestIFile.create(new ByteArrayInputStream(bytes), true, null);
}
manifestIFile.refreshLocal(IResource.DEPTH_ZERO, null); // sync
// Eclipse
// workspace
}

// Update only the headers we touched in PDE's IBundle model
String reqCapValue = manifest.getMainAttributes().getValue(Constants.REQUIRE_CAPABILITY);
if (reqCapValue != null) {
bundle.setHeader(Constants.REQUIRE_CAPABILITY, reqCapValue);
}
// remove BRE if replaced by osgi.ee
manifest.getMainAttributes().remove(new Attributes.Name(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT));

Check warning on line 124 in ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/UpdateHeaderFromAnnotationsOperation.java

View check run for this annotation

Jenkins - eclipse-pde / Compiler

Deprecation

NORMAL: The field Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT is deprecated

} catch (Exception e) {
String message = "Cannot derive header from source";

Check warning on line 127 in ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/UpdateHeaderFromAnnotationsOperation.java

View check run for this annotation

Jenkins - eclipse-pde / Compiler

NLS

NORMAL: $ Non-externalized string literal; it should be followed by //$NON-NLS-
System.out.println(message);
System.out.println(e);
}
}

@SuppressWarnings("deprecation")
private static void mergeRequireCapability(Attributes mainAttributes, Attributes calcAttributes) {
String existingValue = mainAttributes.getValue(Constants.REQUIRE_CAPABILITY);
String newValue = calcAttributes.getValue(Constants.REQUIRE_CAPABILITY);

if (newValue == null) {
return;
}

Parameters additional = OSGiHeader.parseHeader(newValue);
if (additional.containsKey(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE)) {
// remove deprecated header but use the EE namespace
mainAttributes.remove(new Attributes.Name(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT));
}

if (existingValue == null) {
mainAttributes.putValue(Constants.REQUIRE_CAPABILITY, newValue);
} else {
Parameters current = OSGiHeader.parseHeader(existingValue);
if (current.containsKey(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE)) {
// strip duplicate osgi.ee
additional.remove(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE);
}

List<Capability> initialCapabilities = CapReqBuilder.getCapabilitiesFrom(current);
List<Capability> newCapabilities = CapReqBuilder.getCapabilitiesFrom(additional);
if (newCapabilities.isEmpty()) {
return;
}

Set<Capability> mergedCapabilities = new LinkedHashSet<>();
mergedCapabilities.addAll(initialCapabilities);
mergedCapabilities.addAll(newCapabilities);

String merged = mergedCapabilities.stream().map(cap -> String.valueOf(cap).replace("'", "\"")) //$NON-NLS-1$ //$NON-NLS-2$
.collect(Collectors.joining(",")); //$NON-NLS-1$
mainAttributes.putValue(Constants.REQUIRE_CAPABILITY, merged);
}
}
}
Loading