diff options
author | Henrik Rentz-Reichert | 2016-04-16 18:22:47 +0000 |
---|---|---|
committer | Henrik Rentz-Reichert | 2016-04-16 18:22:47 +0000 |
commit | c1b463dd3d17db63ea6465b6c9863ceba17175d9 (patch) | |
tree | 292e8c4ab40cc3faf3d912c094571a82124c5b9d /plugins/org.eclipse.etrice.generator.ui.cdt | |
parent | d4ca6ae5f62de526394f5cf66fd3713640557058 (diff) | |
parent | 64200cdb596bd054a66a90b6892e29e11ab015f2 (diff) | |
download | org.eclipse.etrice-c1b463dd3d17db63ea6465b6c9863ceba17175d9.tar.gz org.eclipse.etrice-c1b463dd3d17db63ea6465b6c9863ceba17175d9.tar.xz org.eclipse.etrice-c1b463dd3d17db63ea6465b6c9863ceba17175d9.zip |
Merge remote-tracking branch 'origin/master'
Reduced o.e.e.genertor.ui.cdt dependency to
org.eclipse.ui.workbench;bundle-version="3.106.0" (was 3.106.2)
Conflicts:
plugins/org.eclipse.etrice.generator.ui.cdt/META-INF/MANIFEST.MF
plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/CProjectConfigurator.java
plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/ProjectConfigurator.java
plugins/org.eclipse.etrice.generator.ui/src/org/eclipse/etrice/generator/ui/configurator/IProjectConfigurator.java
plugins/org.eclipse.etrice.generator.ui/src/org/eclipse/etrice/generator/ui/configurator/ProjectConfigurationDelegator.java
plugins/org.eclipse.etrice.generator.ui/src/org/eclipse/etrice/generator/ui/wizard/NewSetOfModelsWizard.java
plugins/org.eclipse.etrice.generator.ui/src/org/eclipse/etrice/generator/ui/wizard/WizardHelpers.java
plugins/org.eclipse.etrice.generator.ui/src/org/eclipse/etrice/generator/ui/wizard/internal/COptionsPage.java
Change-Id: I9e13b3cd61a480059d674f08e2e5912040a1565a
Diffstat (limited to 'plugins/org.eclipse.etrice.generator.ui.cdt')
4 files changed, 213 insertions, 122 deletions
diff --git a/plugins/org.eclipse.etrice.generator.ui.cdt/META-INF/MANIFEST.MF b/plugins/org.eclipse.etrice.generator.ui.cdt/META-INF/MANIFEST.MF index 4873f6a9f..4846fb4b0 100644 --- a/plugins/org.eclipse.etrice.generator.ui.cdt/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.etrice.generator.ui.cdt/META-INF/MANIFEST.MF @@ -9,10 +9,11 @@ Require-Bundle: org.eclipse.etrice.generator.ui;bundle-version="0.5.0", org.eclipse.cdt.core;bundle-version="5.5.0", org.eclipse.cdt.managedbuilder.core;bundle-version="8.2.0", org.eclipse.equinox.common;bundle-version="3.6.200", - org.eclipse.ui.workbench;bundle-version="3.106.2", + org.eclipse.ui.workbench;bundle-version="3.106.0", org.eclipse.equinox.preferences;bundle-version="3.5.200", org.eclipse.core.commands;bundle-version="3.6.100", org.eclipse.ui.ide;bundle-version="3.10.2", org.eclipse.emf.common;bundle-version="2.10.1", org.eclipse.emf.common.ui;bundle-version="2.9.0" -Import-Package: org.eclipse.core.resources +Import-Package: org.eclipse.core.resources, + org.eclipse.core.runtime.jobs diff --git a/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/CPPProjectConfigurator.java b/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/CPPProjectConfigurator.java index 629b986bf..bf46752ac 100644 --- a/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/CPPProjectConfigurator.java +++ b/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/CPPProjectConfigurator.java @@ -27,7 +27,7 @@ import org.eclipse.core.runtime.IProgressMonitor; * @author Henrik Rentz-Reichert * */ -public class CPPProjectConfigurator extends ProjectConfigurator { +public class CPPProjectConfigurator extends CProjectConfigurator { /* (non-Javadoc) * @see org.eclipse.etrice.generator.ui.cdt.ProjectConfigurator#isApplicable(org.eclipse.core.resources.IProject) @@ -41,9 +41,9 @@ public class CPPProjectConfigurator extends ProjectConfigurator { * @see org.eclipse.etrice.generator.ui.cdt.ProjectConfigurator#getCompilerId() */ @Override - public String getCompilerId() { + public boolean isIncludePathId(String id) { // NOTE: for some reason the "${workspace_loc:/${ProjName}/src-gen}" include path has to be added for the C compiler! - return "cdt.managedbuild.tool.gnu.c.compiler"; + return super.isIncludePathId(id); } /* (non-Javadoc) diff --git a/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/CProjectConfigurator.java b/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/CProjectConfigurator.java index e32944532..c9df00377 100644 --- a/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/CProjectConfigurator.java +++ b/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/CProjectConfigurator.java @@ -26,10 +26,21 @@ import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.settings.model.CIncludePathEntry; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.managedbuilder.core.IConfiguration; +import org.eclipse.cdt.managedbuilder.core.IFolderInfo; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.etrice.generator.ui.wizard.WizardHelpers; +import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.ui.dialogs.IOverwriteQuery; import org.eclipse.ui.wizards.datatransfer.ImportOperation; import org.eclipse.ui.wizards.datatransfer.ZipFileStructureProvider; @@ -40,6 +51,8 @@ import org.eclipse.ui.wizards.datatransfer.ZipFileStructureProvider; */ public class CProjectConfigurator extends ProjectConfigurator { + private static final String C_RUNTIME_FOLDER_NAME = "c-runtime"; + /* (non-Javadoc) * @see org.eclipse.etrice.generator.ui.cdt.ProjectConfigurator#isApplicable(org.eclipse.core.resources.IProject) */ @@ -52,8 +65,15 @@ public class CProjectConfigurator extends ProjectConfigurator { * @see org.eclipse.etrice.generator.ui.cdt.ProjectConfigurator#getCompilerId() */ @Override - public String getCompilerId() { - return "cdt.managedbuild.tool.gnu.c.compiler"; + public boolean isIncludePathId(String id) { + if (id.startsWith("cdt.managedbuild.tool.gnu.c.compiler")) + return true;; + if (id.startsWith("org.eclipse.cdt.msvc.cl.inputType.c")) + return true; + if (id.startsWith("cdt.managedbuild.tool.gnu.c.compiler.input")) + return true; + + return false; } /* (non-Javadoc) @@ -62,7 +82,13 @@ public class CProjectConfigurator extends ProjectConfigurator { @Override public List<CIncludePathEntry> getIncludePaths() { List<CIncludePathEntry> srcIncludes = new ArrayList<CIncludePathEntry>(); - srcIncludes.add(new CIncludePathEntry("/${ProjName}/src-gen", ICSettingEntry.LOCAL)); + srcIncludes.add(new CIncludePathEntry("${workspace_loc:/${ProjName}/src-gen}", ICSettingEntry.LOCAL)); + if (isCopyRuntime()) { + srcIncludes.add(new CIncludePathEntry("${workspace_loc:/${ProjName}/"+C_RUNTIME_FOLDER_NAME+"/src/common}", ICSettingEntry.LOCAL)); + srcIncludes.add(new CIncludePathEntry("${workspace_loc:/${ProjName}/"+C_RUNTIME_FOLDER_NAME+"/src/config}", ICSettingEntry.LOCAL)); + srcIncludes.add(new CIncludePathEntry("${workspace_loc:/${ProjName}/"+C_RUNTIME_FOLDER_NAME+"/src/util}", ICSettingEntry.LOCAL)); + srcIncludes.add(new CIncludePathEntry("${workspace_loc:/${ProjName}/"+C_RUNTIME_FOLDER_NAME+"/src/platforms/"+getPlatform()+"}", ICSettingEntry.LOCAL)); + } return srcIncludes; } @@ -72,14 +98,19 @@ public class CProjectConfigurator extends ProjectConfigurator { @Override public Map<String, String> getProjectRefInfo(ICConfigurationDescription configDescription, String toolChain) { Map<String, String> projectRefInfo = configDescription.getReferenceInfo(); - if (toolChain == MINGW_TOOLCHAIN) { - projectRefInfo.put("org.eclipse.etrice.runtime.c", "cdt.managedbuild.config.gnu.mingw.lib.debug.1978608919"); - projectRefInfo.put("org.eclipse.etrice.modellib.c", "cdt.managedbuild.config.gnu.mingw.lib.debug.847049798"); - } - else if (toolChain == POSIX_TOOLCHAIN) { - projectRefInfo.put("org.eclipse.etrice.runtime.c", "cdt.managedbuild.config.gnu.mingw.lib.debug.1978608919.294295052"); - projectRefInfo.put("org.eclipse.etrice.modellib.c", "cdt.managedbuild.config.gnu.mingw.lib.debug.847049798.58778989"); + + // need to configure these dependencies only if we don't copy the runtime + if (!isCopyRuntime()) { + if (toolChain == MINGW_TOOLCHAIN) { + projectRefInfo.put("org.eclipse.etrice.runtime.c", "cdt.managedbuild.config.gnu.mingw.lib.debug.1978608919"); + projectRefInfo.put("org.eclipse.etrice.modellib.c", "cdt.managedbuild.config.gnu.mingw.lib.debug.847049798"); + } + else if (toolChain == POSIX_TOOLCHAIN) { + projectRefInfo.put("org.eclipse.etrice.runtime.c", "cdt.managedbuild.config.gnu.mingw.lib.debug.1978608919.294295052"); + projectRefInfo.put("org.eclipse.etrice.modellib.c", "cdt.managedbuild.config.gnu.mingw.lib.debug.847049798.58778989"); + } } + return projectRefInfo; } @@ -98,12 +129,20 @@ public class CProjectConfigurator extends ProjectConfigurator { * org.eclipse.core.runtime.IProgressMonitor, java.lang.String) */ @Override - public void copyRuntime(IProject project, IProgressMonitor progressMonitor, - String platform) { + public void copyRuntime(IProject project, IProgressMonitor progressMonitor, String platform) { + copyCRuntime(project, progressMonitor); + copyCModellib(project, progressMonitor); + } + + /** + * @param project + * @param progressMonitor + */ + private void copyCRuntime(IProject project, IProgressMonitor progressMonitor) { ImportOperation importOperation = null; ZipFile zipFile = null; try { - zipFile = WizardHelpers.getRuntimeZip(); + zipFile = WizardHelpers.getCRuntimeZip(); if (zipFile != null) { ZipFileStructureProvider structureProvider = new ZipFileStructureProvider(zipFile); List<?> children = structureProvider.getChildren(structureProvider.getRoot()); @@ -118,7 +157,7 @@ public class CProjectConfigurator extends ProjectConfigurator { } if (current!=null) { importOperation = new ImportOperation( - project.getFullPath().append("c-runtime-src"), + project.getFullPath().append(C_RUNTIME_FOLDER_NAME), current, structureProvider, OVERWRITE_ALL_QUERY); @@ -153,6 +192,87 @@ public class CProjectConfigurator extends ProjectConfigurator { } } + /** + * @param project + * @param progressMonitor + */ + private void copyCModellib(IProject project, IProgressMonitor progressMonitor) { + ZipFile zipFile = null; + try { + zipFile = WizardHelpers.getCModellibZip(); + if (zipFile != null) { + ZipFileStructureProvider structureProvider = new ZipFileStructureProvider(zipFile); + List<?> children = structureProvider.getChildren(structureProvider.getRoot()); + ZipEntry current = null; + for (Object child : children) { + if (child instanceof ZipEntry) { + if (((ZipEntry) child).getName().equals("model/")) { + current = (ZipEntry) child; + break; + } + } + } + if (current!=null) { + children = structureProvider.getChildren(current); + IPath modelFolderPath = getPath(); + for (Object child : children) { + if (child instanceof ZipEntry) { + if (! ((ZipEntry) child).isDirectory()) { + ImportOperation importOperation = new ImportOperation( + modelFolderPath, + child, + structureProvider, + OVERWRITE_ALL_QUERY); + importOperation.setContext(null); + importOperation.run(new SubProgressMonitor(progressMonitor, 1)); + } + } + } + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + final IContainer modelFolder = root.getFolder(modelFolderPath.append("model")); + final IPath newPath = modelFolder.getFullPath().removeLastSegments(1).append("c-modellib"); + + ISchedulingRule rule = ResourcesPlugin.getWorkspace().getRuleFactory().createRule(modelFolder); + WorkspaceModifyOperation operation = new WorkspaceModifyOperation(rule) { + + @Override + protected void execute(IProgressMonitor monitor) throws CoreException, InvocationTargetException, InterruptedException { + + modelFolder.move(newPath, true, monitor); + } + + }; + + try { + operation.run(progressMonitor); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + catch (InvocationTargetException e) { + e.printStackTrace(); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + catch (Exception e) { + e.printStackTrace(); + } + finally { + if (zipFile != null) { + try { + zipFile.close(); + } catch (IOException e) { + // Ignore. + } + } + } + } + protected ZipFile createZipFile(File file) { try { return new ZipFile(file); @@ -163,4 +283,31 @@ public class CProjectConfigurator extends ProjectConfigurator { } return null; } + + /* (non-Javadoc) + * @see org.eclipse.etrice.generator.ui.cdt.ProjectConfigurator#customizeBuildConfig(org.eclipse.cdt.managedbuilder.core.IConfiguration) + */ + @Override + protected void customizeBuildConfig(IProject project, IConfiguration buildConfig) { + if (isCopyRuntime()) { + IFolder folder = project.getFolder(C_RUNTIME_FOLDER_NAME); + folder = folder.getFolder("src"); + folder = folder.getFolder("platforms"); + try { + // exclude from the build if this is a folder and not the selected platform + for (IResource member : folder.members()) { + if (member.getType()==IResource.FOLDER && !member.getName().equals(getPlatform())) { + IPath path = member.getProjectRelativePath(); + IFolderInfo folderInfo = buildConfig.createFolderInfo(path); + if (folderInfo!=null) { + folderInfo.setExclude(true); + } + } + } + } + catch (CoreException e) { + e.printStackTrace(); + } + } + } } diff --git a/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/ProjectConfigurator.java b/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/ProjectConfigurator.java index 3e7286a6f..d42ba2bb0 100644 --- a/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/ProjectConfigurator.java +++ b/plugins/org.eclipse.etrice.generator.ui.cdt/src/org/eclipse/etrice/generator/ui/cdt/ProjectConfigurator.java @@ -37,34 +37,62 @@ public abstract class ProjectConfigurator implements IProjectConfigurator { final static String MINGW_TOOLCHAIN = "MinGW GCC"; final static String POSIX_TOOLCHAIN = "Linux GCC"; + private boolean copyRuntime; + private String platform; + private IPath path; public ProjectConfigurator() { } - public abstract boolean isApplicable(IProject project); - public abstract String getCompilerId(); - public abstract List<CIncludePathEntry> getIncludePaths(); - public abstract Map<String, String> getProjectRefInfo(ICConfigurationDescription configDescription, String toolChain); - public abstract void copyRuntime(IProject project, IProgressMonitor progressMonitor, String platform); + protected abstract boolean isApplicable(IProject project); + protected abstract boolean isIncludePathId(String id); + protected abstract List<CIncludePathEntry> getIncludePaths(); + protected abstract Map<String, String> getProjectRefInfo(ICConfigurationDescription configDescription, String toolChain); + protected abstract void copyRuntime(IProject project, IProgressMonitor progressMonitor, String platform); + protected abstract void customizeBuildConfig(IProject project, IConfiguration buildConfig); @Override - public void configure(IProject project, IProgressMonitor progressMonitor, boolean copyRuntime, String platform) { + public void configure(IProject project, IPath path, boolean copyRuntime, String platform, IProgressMonitor progressMonitor) { + this.copyRuntime = copyRuntime; + this.platform = platform; + this.path = path; + try { - if (!isApplicable(project)) - return; - - if (copyRuntime) { - copyRuntime(project, progressMonitor, platform); + if (isApplicable(project)) { + if (copyRuntime) { + copyRuntime(project, progressMonitor, platform); + } + + configureIncludesAndLibraries(project, progressMonitor); } - - configureIncludesAndLibraries(project, progressMonitor); } catch (CoreException e) { e.printStackTrace(); } } - public void configureIncludesAndLibraries(IProject project, IProgressMonitor progressMonitor) throws CoreException { + /** + * @return the path + */ + protected IPath getPath() { + return path; + } + + /** + * @return the platform + */ + protected String getPlatform() { + return platform; + } + + /** + * @return the copyRuntime + */ + protected boolean isCopyRuntime() { + return copyRuntime; + } + + protected void configureIncludesAndLibraries(IProject project, IProgressMonitor progressMonitor) throws CoreException { ICProjectDescription projectDescription = CoreModel.getDefault().getProjectDescription(project, true); @@ -82,7 +110,7 @@ public abstract class ProjectConfigurator implements IProjectConfigurator { for (ICConfigurationDescription configDescription : projectDescription.getConfigurations()) { if (configDescription.getId() == null) continue; - + IConfiguration buildConfig = ManagedBuildManager.getConfigurationForDescription(configDescription); String toolChain = ""; if (MINGW_TOOLCHAIN.equals(buildConfig.getToolChain().getName())) @@ -94,6 +122,8 @@ public abstract class ProjectConfigurator implements IProjectConfigurator { if (folderInfo!=null) { folderInfo.setExclude(true); } + + customizeBuildConfig(project, buildConfig); // set project references /* @@ -111,7 +141,7 @@ public abstract class ProjectConfigurator implements IProjectConfigurator { continue; // set source includes - if (setting.getId().startsWith(getCompilerId())) { + if (isIncludePathId(setting.getId())) { addSettings(setting, ICSettingEntry.INCLUDE_PATH, getIncludePaths()); } @@ -133,93 +163,6 @@ public abstract class ProjectConfigurator implements IProjectConfigurator { CoreModel.getDefault().setProjectDescription(project, projectDescription); } - // public static void addIncludePathsAndLibraries(IProject project) - // throws CoreException { - // if (!project.hasNature("org.eclipse.cdt.core.cnature")) - // return; - // - // IWorkspace workspace = ResourcesPlugin.getWorkspace(); - // IProject runtime = workspace.getRoot().getProject( - // "org.eclipse.etrice.runtime.c"); - // IFolder common = runtime.getFolder("src/common"); - // IFolder config = runtime.getFolder("src/config"); - // IFolder posix = runtime.getFolder("src/platforms/MT_POSIX_GENERIC_GCC"); - // IFolder mingw = runtime.getFolder("src/platforms/MT_WIN_MinGW"); - // IFolder src_gen = project.getFolder("src-gen"); - // IFolder mingw_debug = project.getFolder("MinGWDebug"); - // IFolder mingw_release = project.getFolder("MinGWRelease"); - // IFolder posix_debug = project.getFolder("PosixDebug"); - // IFolder posix_release = project.getFolder("PosixRelease"); - // - // ICProjectDescription projectDescription = CoreModel.getDefault() - // .getProjectDescription(project, true); - // ICConfigurationDescription configDecriptions[] = projectDescription - // .getConfigurations(); - // - // for (ICConfigurationDescription configDescription : configDecriptions) { - // ICFolderDescription projectRoot = configDescription - // .getRootFolderDescription(); - // ICLanguageSetting[] settings = projectRoot.getLanguageSettings(); - // for (ICLanguageSetting setting : settings) { - // if (!"org.eclipse.cdt.core.gcc".equals(setting.getLanguageId())) { - // continue; - // } - // - // ICTargetPlatformSetting tgt = configDescription - // .getTargetPlatformSetting(); - // String id = tgt.getId(); - // - // ArrayList<ICLanguageSettingEntry> includes = new - // ArrayList<ICLanguageSettingEntry>(); - // includes.add(new CIncludePathEntry(src_gen, - // ICSettingEntry.LOCAL)); - // includes.add(new CIncludePathEntry(common, ICSettingEntry.LOCAL)); - // includes.add(new CIncludePathEntry(config, ICSettingEntry.LOCAL)); - // if (id.startsWith("cdt.managedbuild.target.gnu.platform.mingw.exe")) { - // includes.add(new CIncludePathEntry(mingw, - // ICSettingEntry.LOCAL)); - // } else if (id - // .startsWith("cdt.managedbuild.target.gnu.platform.posix.exe")) { - // includes.add(new CIncludePathEntry(posix, - // ICSettingEntry.LOCAL)); - // } - // addSettings(setting, ICSettingEntry.INCLUDE_PATH, includes); - // - // List<? extends ICLanguageSettingEntry> libPaths = null; - // if - // (id.startsWith("cdt.managedbuild.target.gnu.platform.mingw.exe.debug")) { - // libPaths = Collections.singletonList(new CLibraryPathEntry( - // mingw_debug, ICSettingEntry.LOCAL)); - // } else if (id - // .startsWith("cdt.managedbuild.target.gnu.platform.mingw.exe.release")) { - // libPaths = Collections.singletonList(new CLibraryPathEntry( - // mingw_release, ICSettingEntry.LOCAL)); - // } else if (id - // .startsWith("cdt.managedbuild.target.gnu.platform.posix.exe.debug")) { - // libPaths = Collections.singletonList(new CLibraryPathEntry( - // posix_debug, ICSettingEntry.LOCAL)); - // } else if (id - // .startsWith("cdt.managedbuild.target.gnu.platform.posix.exe.release")) { - // libPaths = Collections.singletonList(new CLibraryPathEntry( - // posix_release, ICSettingEntry.LOCAL)); - // } - // if (libPaths != null) - // addSettings(setting, ICSettingEntry.LIBRARY_PATH, libPaths); - // - // List<? extends ICLanguageSettingEntry> libs = Collections - // .singletonList(new CLibraryFileEntry( - // "org.eclipse.etrice.runtime.c", 0)); - // addSettings(setting, ICSettingEntry.LIBRARY_FILE, libs); - // } - // } - // try { - // CoreModel.getDefault().setProjectDescription(project, - // projectDescription); - // } catch (CoreException e) { - // e.printStackTrace(); - // } - // } - /** * Appends entries at the end, preserves order, prevents new duplicates. */ |