mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-09-10 12:03:16 +02:00
Bug 229085 / Bug 100844 - Project references should pick up -l library_name automatically.
- IOption#LIBRARIES mapped to cdt.core.settings entry LIBRARY_FILE - Configuration sets up exported library file by default - JavaDoc + Generics
This commit is contained in:
parent
cf75226ce6
commit
f63630ee74
12 changed files with 332 additions and 136 deletions
|
@ -2,7 +2,7 @@ Manifest-Version: 1.0
|
|||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: %pluginName
|
||||
Bundle-SymbolicName: org.eclipse.cdt.managedbuilder.core; singleton:=true
|
||||
Bundle-Version: 6.0.0.qualifier
|
||||
Bundle-Version: 6.1.0.qualifier
|
||||
Bundle-Activator: org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin
|
||||
Bundle-Vendor: %providerName
|
||||
Bundle-Localization: plugin
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2003, 2007 IBM Corporation and others.
|
||||
* Copyright (c) 2003, 2009 IBM Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
|
@ -8,12 +8,12 @@
|
|||
* Contributors:
|
||||
* IBM - Initial API and implementation
|
||||
* ARM Ltd. - basic tooltip support
|
||||
* James Blackburn (Broadcom Corp.)
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.managedbuilder.core;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Basic Tool / Toolchain Option type.
|
||||
*/
|
||||
public interface IOption extends IBuildObject {
|
||||
// Type for the value of the option
|
||||
|
@ -23,10 +23,28 @@ public interface IOption extends IBuildObject {
|
|||
public static final int STRING_LIST = 3;
|
||||
public static final int INCLUDE_PATH = 4;
|
||||
public static final int PREPROCESSOR_SYMBOLS = 5;
|
||||
/**
|
||||
* String list of library names to link against searched for
|
||||
* via LIBRARY_PATHS by the linker. In the GNU
|
||||
* toolchain these correspond to -l{lib_name}. <br/>
|
||||
* This option type is persisted / referenced by the name
|
||||
* {@link IOption#TYPE_LIB}
|
||||
*/
|
||||
public static final int LIBRARIES = 6;
|
||||
public static final int OBJECTS = 7;
|
||||
public static final int INCLUDE_FILES = 8;
|
||||
/**
|
||||
* String list of library search paths <br/>
|
||||
* This option type is persisted / referenced by the name
|
||||
* {@link IOption #TYPE_LIB_PATHS}
|
||||
*/
|
||||
public static final int LIBRARY_PATHS = 9;
|
||||
/**
|
||||
* String list of absolute path to libraries.
|
||||
* Not currently used by the GNU integration <br/>
|
||||
* This option type is persisted / referenced by the name 'libFiles'
|
||||
* {@link IOption #TYPE_LIB_FILES}
|
||||
*/
|
||||
public static final int LIBRARY_FILES = 10;
|
||||
public static final int MACRO_FILES = 11;
|
||||
|
||||
|
@ -36,7 +54,7 @@ public interface IOption extends IBuildObject {
|
|||
public static final int UNDEF_LIBRARY_PATHS = -LIBRARY_PATHS;
|
||||
public static final int UNDEF_LIBRARY_FILES = -LIBRARY_FILES;
|
||||
public static final int UNDEF_MACRO_FILES = -MACRO_FILES;
|
||||
|
||||
|
||||
// Browse type
|
||||
public static final int BROWSE_NONE = 0;
|
||||
public static final String NONE = "none"; //$NON-NLS-1$
|
||||
|
|
|
@ -60,6 +60,7 @@ import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry;
|
|||
import org.eclipse.cdt.core.settings.model.ICMultiConfigDescription;
|
||||
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
|
||||
import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager;
|
||||
import org.eclipse.cdt.core.settings.model.ICSettingEntry;
|
||||
import org.eclipse.cdt.core.settings.model.XmlStorageUtil;
|
||||
import org.eclipse.cdt.core.settings.model.extension.CConfigurationData;
|
||||
import org.eclipse.cdt.managedbuilder.buildproperties.IBuildProperty;
|
||||
|
@ -3910,7 +3911,12 @@ public class ManagedBuildManager extends AbstractCExtension {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert the IOption integer type ID to the {@link ICSettingEntry#getKind()} type ID
|
||||
* @param type {@link IOption#getValueType()}
|
||||
* @return ICSettingEntry type
|
||||
*/
|
||||
public static int optionTypeToEntryKind(int type){
|
||||
switch(type){
|
||||
case IOption.INCLUDE_PATH:
|
||||
|
@ -3921,16 +3927,20 @@ public class ManagedBuildManager extends AbstractCExtension {
|
|||
return ICLanguageSettingEntry.INCLUDE_FILE;
|
||||
case IOption.LIBRARY_PATHS:
|
||||
return ICLanguageSettingEntry.LIBRARY_PATH;
|
||||
case IOption.LIBRARIES:
|
||||
case IOption.LIBRARY_FILES:
|
||||
return ICLanguageSettingEntry.LIBRARY_FILE;
|
||||
case IOption.MACRO_FILES:
|
||||
return ICLanguageSettingEntry.MACRO_FILE;
|
||||
// case IOption.LIBRARIES:
|
||||
// return ICLanguageSettingEntry.LIBRARY_PATH;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert the IOption integer type ID to the {@link ICSettingEntry#getKind()} type ID
|
||||
* @param type {@link IOption#getValueType()}
|
||||
* @return ICSettingEntry type
|
||||
*/
|
||||
public static int optionUndefTypeToEntryKind(int type){
|
||||
switch(type){
|
||||
case IOption.UNDEF_INCLUDE_PATH:
|
||||
|
@ -3945,8 +3955,6 @@ public class ManagedBuildManager extends AbstractCExtension {
|
|||
return ICLanguageSettingEntry.LIBRARY_FILE;
|
||||
case IOption.UNDEF_MACRO_FILES:
|
||||
return ICLanguageSettingEntry.MACRO_FILE;
|
||||
// case IOption.LIBRARIES:
|
||||
// return ICLanguageSettingEntry.LIBRARY_PATH;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2003, 2007 IBM Corporation and others.
|
||||
* Copyright (c) 2003, 2009 IBM Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
|
@ -7,6 +7,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* IBM Rational Software - Initial API and implementation
|
||||
* James Blackburn (Broadcom Corp.)
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.managedbuilder.core;
|
||||
|
||||
|
@ -210,6 +211,24 @@ public class ManagedBuilderCorePlugin extends Plugin {
|
|||
log(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a String error to the error log
|
||||
* @param str string error message to log
|
||||
* @since 6.1
|
||||
*/
|
||||
public static void error(String str) {
|
||||
log(new Status(IStatus.ERROR, getUniqueIdentifier(), IStatus.OK, str, new Exception()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a String info message to the log
|
||||
* @param str string info message to log
|
||||
* @since 6.1
|
||||
*/
|
||||
public static void info(String str) {
|
||||
log(new Status(IStatus.INFO, getUniqueIdentifier(), IStatus.OK, str, new Exception()));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2003, 2008 IBM Corporation and others.
|
||||
* Copyright (c) 2003, 2009 IBM Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
|
@ -7,6 +7,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* IBM - Initial API and implementation
|
||||
* James Blackburn (Broadcom Corp.)
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.managedbuilder.internal.core;
|
||||
|
||||
|
@ -25,6 +26,7 @@ import org.eclipse.cdt.build.core.scannerconfig.ICfgScannerConfigBuilderInfo2Set
|
|||
import org.eclipse.cdt.build.internal.core.scannerconfig.CfgDiscoveredPathManager.PathInfoCache;
|
||||
import org.eclipse.cdt.core.CCorePlugin;
|
||||
import org.eclipse.cdt.core.settings.model.CIncludePathEntry;
|
||||
import org.eclipse.cdt.core.settings.model.CLibraryFileEntry;
|
||||
import org.eclipse.cdt.core.settings.model.CLibraryPathEntry;
|
||||
import org.eclipse.cdt.core.settings.model.CSourceEntry;
|
||||
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
|
||||
|
@ -2648,6 +2650,13 @@ public class Configuration extends BuildObject implements IConfiguration, IBuild
|
|||
return getRootFolderInfo().buildsFileType(srcExt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for contributing 'external' settings back to the core for use
|
||||
* by referenced projects.
|
||||
*
|
||||
* In this case it returns Include, Library path & Library File settings
|
||||
* to be used be references for linking the output of this library project
|
||||
*/
|
||||
public void exportArtifactInfo(){
|
||||
if(isExtensionConfig)
|
||||
return;
|
||||
|
@ -2663,10 +2672,12 @@ public class Configuration extends BuildObject implements IConfiguration, IBuild
|
|||
ICOutputEntry entries[] = getConfigurationData().getBuildData().getOutputDirectories();
|
||||
IPath path = getOwner().getFullPath();
|
||||
|
||||
List list = new ArrayList(entries.length + 1);
|
||||
List<ICSettingEntry> list = new ArrayList<ICSettingEntry>(entries.length + 1);
|
||||
|
||||
// Add project level include path
|
||||
list.add(new CIncludePathEntry(path.toString(), ICLanguageSettingEntry.VALUE_WORKSPACE_PATH));
|
||||
|
||||
// Add Build output path as an exported library path
|
||||
entries = CDataUtil.resolveEntries(entries, des);
|
||||
for(int i = 0; i < entries.length; i++){
|
||||
ICOutputEntry out = entries[i];
|
||||
|
@ -2675,17 +2686,22 @@ public class Configuration extends BuildObject implements IConfiguration, IBuild
|
|||
IPath p = new Path(value);
|
||||
if(!p.isAbsolute())
|
||||
value = getOwner().getFullPath().append(value).toString();
|
||||
|
||||
ICLibraryPathEntry lib = new CLibraryPathEntry(value, out.getFlags() & (~ICSettingEntry.RESOLVED));
|
||||
list.add(lib);
|
||||
}
|
||||
|
||||
des.createExternalSetting(null, null, null, (ICLanguageSettingEntry[])list.toArray(new ICLanguageSettingEntry[list.size()]));
|
||||
|
||||
// Add 'libs' artifact names themselves
|
||||
ICSettingEntry[] libFile = new ICSettingEntry[] {new CLibraryFileEntry(getArtifactName(), 0)};
|
||||
libFile = CDataUtil.resolveEntries(libFile, des);
|
||||
list.add(libFile[0]);
|
||||
|
||||
// Contribute the settings back as 'exported'
|
||||
des.createExternalSetting(null, null, null, list.toArray(new ICSettingEntry[list.size()]));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean supportsBuild(boolean managed) {
|
||||
return supportsBuild(managed, true);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2007 Intel Corporation and others.
|
||||
* Copyright (c) 2007, 2009 Intel Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
|
@ -7,6 +7,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Intel Corporation - Initial API and implementation
|
||||
* James Blackburn (Broadcom Corp.)
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.managedbuilder.internal.dataprovider;
|
||||
|
||||
|
@ -28,6 +29,7 @@ import org.eclipse.cdt.core.settings.model.util.CDataUtil;
|
|||
import org.eclipse.cdt.core.settings.model.util.SettingsSet;
|
||||
import org.eclipse.cdt.core.settings.model.util.SettingsSet.EntryInfo;
|
||||
import org.eclipse.cdt.core.settings.model.util.SettingsSet.SettingLevel;
|
||||
import org.eclipse.cdt.managedbuilder.core.BuildException;
|
||||
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
|
||||
import org.eclipse.cdt.managedbuilder.core.IEnvVarBuildPath;
|
||||
import org.eclipse.cdt.managedbuilder.core.IOption;
|
||||
|
@ -50,6 +52,10 @@ import org.eclipse.core.resources.IResource;
|
|||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
|
||||
/**
|
||||
* BuildEntryStorage has a handle back to the BuildLanguageData
|
||||
* to allow checking on which language entries are actually defined.
|
||||
*/
|
||||
public class BuildEntryStorage extends AbstractEntryStorage {
|
||||
private static final int USER_ENTRIES_LEVEL = 0;
|
||||
private static final int ENV_ENTRIES_LEVEL = 1;
|
||||
|
@ -186,6 +192,11 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return scanner discovered entries (level 2)
|
||||
* @param flags
|
||||
* @return
|
||||
*/
|
||||
private ICLanguageSettingEntry[] getDiscoveredEntries(int flags){
|
||||
ICLanguageSettingEntry[] entries = ProfileInfoProvider.getInstance().getEntryValues(fLangData, getKind(), flags);
|
||||
if(entries == null || entries.length == 0){
|
||||
|
@ -209,6 +220,16 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
: new SupplierBasedCdtVariableSubstitutor(ci, "", " "); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user entries (level 0)
|
||||
*
|
||||
* The UserEntryInfo[] is an array of all user entries from all the options
|
||||
* applicable to the language setting entry kind.
|
||||
* @param flags
|
||||
* @param usr
|
||||
* @param emptyValuesInfos list to which unresolved entries are added
|
||||
* @return UserEntryInfo[] (never null)
|
||||
*/
|
||||
private UserEntryInfo[] getUserEntries(int flags, boolean usr, List<EmptyEntryInfo> emptyValuesInfos){
|
||||
IOption options[] = fLangData.getOptionsForKind(getKind());
|
||||
if(options.length > 0){
|
||||
|
@ -223,10 +244,12 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
OptionStringValue ve = list.get(j);
|
||||
OptionStringValue[] rVes = resolve(ve, option, bSVarsSubst);
|
||||
if(rVes.length == 0){
|
||||
// If not resolved, add EmptyEntryInfo based off the value entry
|
||||
if(emptyValuesInfos != null){
|
||||
emptyValuesInfos.add(new EmptyEntryInfo(ve, j));
|
||||
}
|
||||
} else {
|
||||
// If resolved, add each resolved entry as a separate UserEntryInfo
|
||||
boolean isMultiple = rVes.length > 1;
|
||||
List<UserEntryInfo> sequense = isMultiple ? new ArrayList<UserEntryInfo>(rVes.length) : null;
|
||||
for (OptionStringValue rVe : rVes) {
|
||||
|
@ -278,6 +301,11 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes non-absolute paths relative to the build directory
|
||||
* @param info
|
||||
* @return
|
||||
*/
|
||||
private PathInfo fromBuildToProj(PathInfo info){
|
||||
if(info.isAbsolute())
|
||||
return info;
|
||||
|
@ -334,6 +362,11 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return env envtries (level 1)
|
||||
* @param flags
|
||||
* @return
|
||||
*/
|
||||
private ICLanguageSettingEntry[] getEnvEntries(int flags){
|
||||
String paths[] = null;
|
||||
int kind = getKind();
|
||||
|
@ -361,26 +394,53 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
return new ICLanguageSettingEntry[0];
|
||||
}
|
||||
|
||||
private ICLanguageSettingEntry createUserEntry(IOption option, OptionStringValue optionValue, int flags, SupplierBasedCdtVariableSubstitutor subst){
|
||||
// private ICLanguageSettingEntry createUserEntry(Option option, String optionValue, int flags){
|
||||
int kind = getKind();
|
||||
/**
|
||||
* Create an ICLanguageSettingEntry based on the passed in Option
|
||||
* @param option
|
||||
* @param optionValue
|
||||
* @param flags
|
||||
* @param subst
|
||||
* @return
|
||||
*/
|
||||
private ICLanguageSettingEntry createUserEntry(Option option, OptionStringValue optionValue, int flags, SupplierBasedCdtVariableSubstitutor subst){
|
||||
final int kind = getKind();
|
||||
|
||||
ICLanguageSettingEntry entry = null;
|
||||
if (kind == ICSettingEntry.MACRO) {
|
||||
String nv[] = macroNameValueFromValue(optionValue.getValue());
|
||||
return new CMacroEntry(nv[0], nv[1], flags);
|
||||
}
|
||||
|
||||
IPath srcPath = null, srcRootPath = null, srcPrefixMapping = null;
|
||||
|
||||
switch (kind){
|
||||
case ICSettingEntry.MACRO:
|
||||
String nv[] = macroNameValueFromValue(optionValue.getValue());
|
||||
// String nv[] = macroNameValueFromValue(optionValue);
|
||||
IOptionPathConverter optionPathConverter = fLangData.getTool().getOptionPathConverter();
|
||||
// Create a PathInfo entry representing the optionValue
|
||||
PathInfo pInfo = optionPathValueToEntry(optionValue.getValue(), subst);
|
||||
|
||||
entry = new CMacroEntry(nv[0], nv[1], flags);
|
||||
break;
|
||||
// case ICSettingEntry.INCLUDE_PATH:
|
||||
// case ICSettingEntry.INCLUDE_FILE:
|
||||
// case ICSettingEntry.MACRO_FILE:
|
||||
// case ICSettingEntry.LIBRARY_PATH:
|
||||
case ICSettingEntry.LIBRARY_FILE:
|
||||
if(pInfo.isWorkspacePath()){
|
||||
flags |= ICSettingEntry.VALUE_WORKSPACE_PATH;
|
||||
} else if (optionPathConverter != null){
|
||||
IPath path = optionPathConverter.convertToPlatformLocation(pInfo.getUnresolvedPath(), option, fLangData.getTool());
|
||||
if(path != null){
|
||||
pInfo = new PathInfo(path.toString(), false, subst);
|
||||
}
|
||||
}
|
||||
|
||||
// make non absolute paths relative to the build directory
|
||||
if (getOptionType(option) != IOption.LIBRARIES)
|
||||
pInfo = fromBuildToProj(pInfo);
|
||||
else {
|
||||
// The IOption.LIBRARIES type is morphed to => ICSettingEntyr#LIBRARY_FILE
|
||||
// It *isn't* a workspace path!
|
||||
flags &= ~ICSettingEntry.VALUE_WORKSPACE_PATH;
|
||||
pInfo = new PathInfo(optionValue.getValue(), false, subst);
|
||||
}
|
||||
|
||||
// Library files are special, they potentially know about their source Prefix & Root
|
||||
// The build system has two different types for storing Library Files: IOption.LIBRARIES
|
||||
// && IOption.LIBRARY_FILES. We map both of these to a CLibraryFileEntry, and mark the
|
||||
// difference with a flag isRawEntry Type.
|
||||
if (kind == ICSettingEntry.LIBRARY_FILE) {
|
||||
// Handle source types
|
||||
String tmp = optionValue.getSourceAttachmentPath();
|
||||
if(tmp != null)
|
||||
srcPath = new Path(tmp);
|
||||
|
@ -390,29 +450,9 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
tmp = optionValue.getSourceAttachmentPrefixMapping();
|
||||
if(tmp != null)
|
||||
srcPrefixMapping = new Path(tmp);
|
||||
//do not break
|
||||
//$FALL-THROUGH$
|
||||
default:
|
||||
IOptionPathConverter optionPathConverter = fLangData.getTool().getOptionPathConverter();
|
||||
PathInfo pInfo = optionPathValueToEntry(optionValue.getValue(), subst);
|
||||
// Object[] v = optionPathValueToEntry(stripQuotes(optionValue.getValue()));
|
||||
// Object[] v = optionPathValueToEntry(optionValue);
|
||||
|
||||
if(pInfo.isWorkspacePath()){
|
||||
flags |= ICSettingEntry.VALUE_WORKSPACE_PATH;
|
||||
} else if (optionPathConverter != null){
|
||||
IPath path = optionPathConverter.convertToPlatformLocation(pInfo.getUnresolvedPath(), option, fLangData.getTool());
|
||||
if(path != null){
|
||||
pInfo = new PathInfo(path.toString(), false, subst);
|
||||
}
|
||||
}
|
||||
|
||||
pInfo = fromBuildToProj(pInfo);
|
||||
|
||||
entry = (ICLanguageSettingEntry)CDataUtil.createEntry(kind, pInfo.getUnresolvedPath(), null, null, flags, srcPath, srcRootPath, srcPrefixMapping);
|
||||
break;
|
||||
}
|
||||
return entry;
|
||||
|
||||
return (ICLanguageSettingEntry)CDataUtil.createEntry(kind, pInfo.getUnresolvedPath(), null, null, flags, srcPath, srcRootPath, srcPrefixMapping);
|
||||
}
|
||||
|
||||
private OptionStringValue createOptionValue(IOption option, UserEntryInfo info, SupplierBasedCdtVariableSubstitutor subst){
|
||||
|
@ -485,13 +525,29 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
|
||||
result = ManagedBuildManager.fullPathToLocation(result);
|
||||
} else {
|
||||
pInfo = fromProjToBuild(pInfo);
|
||||
// Persisting to the LIBRARIES option type doens't require path translation
|
||||
// as this is just the name of a library, not the path to it...
|
||||
if (getOptionType(option) != IOption.LIBRARIES)
|
||||
pInfo = fromProjToBuild(pInfo);
|
||||
result = pInfo.getUnresolvedPath();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param option
|
||||
* @return the option type for the option
|
||||
*/
|
||||
private static int getOptionType(IOption option) {
|
||||
try {
|
||||
return option.getValueType();
|
||||
} catch (BuildException e) {
|
||||
ManagedBuilderCorePlugin.log(e);
|
||||
}
|
||||
return IOption.STRING;
|
||||
}
|
||||
|
||||
private static String doubleQuotePath(String pathName, boolean nullIfNone) {
|
||||
/* Trim */
|
||||
pathName = pathName.trim();
|
||||
|
@ -558,7 +614,11 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
private void setUserEntries(UserEntryInfo[] entries, List<EmptyEntryInfo> emptyEntryInfos){
|
||||
int kind = getKind();
|
||||
IOption options[] = fLangData.getOptionsForKind(kind);
|
||||
if(options.length != 0){
|
||||
// We don't expect more than one option to manage a particular entry kind, though it
|
||||
// is theoretically possible... Add a trace for Toolchain developers
|
||||
if (options.length > 1)
|
||||
ManagedBuilderCorePlugin.error("Unexpected error: Warning more than one options found for kind " + getKind()); //$NON-NLS-1$
|
||||
if(options.length != 0) {
|
||||
IOption option = options[0];
|
||||
OptionStringValue[] optValue;
|
||||
if(entries.length != 0){
|
||||
|
@ -604,6 +664,16 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
return list.toArray(new UserEntryInfo[list.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method iterates over the passed in UserEntryInfo[]. The point of it is
|
||||
* to collapse entries which are contained in a given UserEntry's sequence into a single
|
||||
* UserEntryInfo.
|
||||
*
|
||||
* FIXME: As far as I can see info.fSequense only ever has a single entry in it...
|
||||
* see UserEntryInfo constructor => this method doesn't accomplish anything useful
|
||||
* @param infos
|
||||
* @return
|
||||
*/
|
||||
private UserEntryInfo[] combineSequenses(UserEntryInfo infos[]){
|
||||
if(infos.length == 0)
|
||||
return infos;
|
||||
|
@ -629,10 +699,10 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
if(match){
|
||||
i = i + seqSize - 1;
|
||||
} else {
|
||||
infos[i] = createDesecuencedEntry(info);
|
||||
infos[i] = createDesequencedEntry(info);
|
||||
for(int k = i + 1; k < infos.length; k++){
|
||||
if(infos[k].fSequense == info.fSequense)
|
||||
infos[k] = createDesecuencedEntry(infos[k]);
|
||||
infos[k] = createDesequencedEntry(infos[k]);
|
||||
}
|
||||
info = infos[i];
|
||||
}
|
||||
|
@ -643,7 +713,7 @@ public class BuildEntryStorage extends AbstractEntryStorage {
|
|||
return list.toArray(new UserEntryInfo[list.size()]);
|
||||
}
|
||||
|
||||
private static UserEntryInfo createDesecuencedEntry(UserEntryInfo info){
|
||||
private static UserEntryInfo createDesequencedEntry(UserEntryInfo info){
|
||||
OptionStringValue resolvedValue = info.fBsResolvedValue;
|
||||
if(resolvedValue != null){
|
||||
String v = doubleQuotePath(resolvedValue.getValue(), true);
|
||||
|
|
|
@ -12,7 +12,9 @@ package org.eclipse.cdt.managedbuilder.internal.dataprovider;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry;
|
||||
import org.eclipse.cdt.core.settings.model.extension.CLanguageData;
|
||||
|
@ -32,17 +34,33 @@ import org.eclipse.cdt.managedbuilder.internal.core.FolderInfo;
|
|||
import org.eclipse.cdt.managedbuilder.internal.core.InputType;
|
||||
import org.eclipse.cdt.managedbuilder.internal.core.ResourceConfiguration;
|
||||
|
||||
/**
|
||||
* This class holds the language data for managed build tool
|
||||
*
|
||||
* It current holds both the main kind => BuildEntryStorage
|
||||
* mapping as well as mappings on the currently undef'd kinds
|
||||
* (e.g. a language setting entry defined by scanner discovery
|
||||
* but later re-defined by a build system setting )
|
||||
*/
|
||||
public class BuildLanguageData extends CLanguageData {
|
||||
private static final IOption[] EMPTY_OPTION_ARRAY = new IOption[0];
|
||||
|
||||
private final String fId;
|
||||
private ITool fTool;
|
||||
private IInputType fInputType;
|
||||
private KindBasedStore fKindToOptionArrayStore = new KindBasedStore();
|
||||
private KindBasedStore fKindToUndefOptionArrayStore = new KindBasedStore();
|
||||
private static final IOption[] EMPTY_OPTION_ARRAY = new IOption[0];
|
||||
private boolean fOptionStoreInited;
|
||||
|
||||
/** The main kind => BuildEntryStorage store
|
||||
* The BuildEntryStorage calls back to this BuildLanguageData
|
||||
* to work out which entries are actually (un)defined. */
|
||||
private KindBasedStore<BuildEntryStorage> fKindToEntryStore = new KindBasedStore<BuildEntryStorage>();
|
||||
|
||||
/** Indicates that the option array stores have been inited */
|
||||
private volatile boolean fOptionStoreInited;
|
||||
private KindBasedStore<IOption[]> fKindToOptionArrayStore = new KindBasedStore<IOption[]>();
|
||||
private KindBasedStore<IOption[]> fKindToUndefOptionArrayStore = new KindBasedStore<IOption[]>();
|
||||
|
||||
// private Map fKindToEntryArrayMap = new HashMap();
|
||||
// private ProfileInfoProvider fDiscoveredInfo;
|
||||
private KindBasedStore fKindToEntryStore = new KindBasedStore();
|
||||
private String fId;
|
||||
|
||||
|
||||
public BuildLanguageData(ITool tool, IInputType inType){
|
||||
|
@ -88,7 +106,7 @@ public class BuildLanguageData extends CLanguageData {
|
|||
if(getOptionsForKind(kind).length == 0 && isToolChainDiscoveryProfile())
|
||||
return null;
|
||||
|
||||
BuildEntryStorage storage = (BuildEntryStorage)fKindToEntryStore.get(kind);
|
||||
BuildEntryStorage storage = fKindToEntryStore.get(kind);
|
||||
if(storage == null){
|
||||
storage = new BuildEntryStorage(kind, this);
|
||||
fKindToEntryStore.put(kind, storage);
|
||||
|
@ -132,7 +150,7 @@ public class BuildLanguageData extends CLanguageData {
|
|||
}
|
||||
|
||||
public ICLanguageSettingEntry[] getEntries(int kinds) {
|
||||
List list = new ArrayList();
|
||||
List<ICLanguageSettingEntry> list = new ArrayList<ICLanguageSettingEntry>();
|
||||
|
||||
if((kinds & ICLanguageSettingEntry.INCLUDE_PATH) != 0) {
|
||||
BuildEntryStorage storage = getEntryStorage(ICLanguageSettingEntry.INCLUDE_PATH);
|
||||
|
@ -160,7 +178,7 @@ public class BuildLanguageData extends CLanguageData {
|
|||
storage.getEntries(list);
|
||||
}
|
||||
|
||||
return (ICLanguageSettingEntry[])list.toArray(new ICLanguageSettingEntry[list.size()]);
|
||||
return list.toArray(new ICLanguageSettingEntry[list.size()]);
|
||||
}
|
||||
|
||||
public void updateInputType(IInputType type){
|
||||
|
@ -178,63 +196,62 @@ public class BuildLanguageData extends CLanguageData {
|
|||
return fInputType != null ? fInputType.getSourceExtensions(fTool) : fTool.getPrimaryInputExtensions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSupportedEntryKinds() {
|
||||
KindBasedStore store = getKindToOptionArrayStore();
|
||||
IKindBasedInfo infos[] = store.getContents();
|
||||
KindBasedStore<IOption[]> store = getKindToOptionArrayStore();
|
||||
IKindBasedInfo<IOption[]>[] infos = store.getContents();
|
||||
int kinds = 0;
|
||||
for(int i = 0; i < infos.length; i++){
|
||||
if(((IOption[])infos[i].getInfo()).length > 0)
|
||||
if(infos[i].getInfo().length > 0)
|
||||
kinds |= infos[i].getKind();
|
||||
}
|
||||
return kinds;
|
||||
}
|
||||
|
||||
private KindBasedStore getKindToOptionArrayStore(){
|
||||
private KindBasedStore<IOption[]> getKindToOptionArrayStore(){
|
||||
initOptionStores();
|
||||
return fKindToOptionArrayStore;
|
||||
}
|
||||
|
||||
private void initOptionStores(){
|
||||
if(!fOptionStoreInited){
|
||||
initOptionStoresSynch();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void initOptionStoresSynch(){
|
||||
if(!fOptionStoreInited){
|
||||
calculateKindToOptionArrayStore();
|
||||
calculateKindToUndefOptionArrayStore();
|
||||
fOptionStoreInited = true;
|
||||
private void initOptionStores() {
|
||||
if(!fOptionStoreInited) {
|
||||
synchronized (this) {
|
||||
if(!fOptionStoreInited) {
|
||||
calculateKindToOptionArrayStore();
|
||||
calculateKindToUndefOptionArrayStore();
|
||||
fOptionStoreInited = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private KindBasedStore getKindToUndefOptionArrayStore(){
|
||||
private KindBasedStore<IOption[]> getKindToUndefOptionArrayStore(){
|
||||
initOptionStores();
|
||||
return fKindToUndefOptionArrayStore;
|
||||
}
|
||||
|
||||
private void calculateKindToOptionArrayStore(){
|
||||
fKindToOptionArrayStore.clear();
|
||||
Map<Integer, List<IOption>> kindToOptionList = new HashMap<Integer, List<IOption>>();
|
||||
IOption options[] = fTool.getOptions();
|
||||
for(int i = 0; i < options.length; i++){
|
||||
IOption option = options[i];
|
||||
for (final IOption option : options) {
|
||||
try {
|
||||
int type = option.getValueType();
|
||||
int entryKind = ManagedBuildManager.optionTypeToEntryKind(type);
|
||||
Integer entryKind = ManagedBuildManager.optionTypeToEntryKind(option.getValueType());
|
||||
if(entryKind != 0){
|
||||
getOptionList(fKindToOptionArrayStore, entryKind).add(option);
|
||||
if (!kindToOptionList.containsKey(entryKind))
|
||||
kindToOptionList.put(entryKind, new ArrayList<IOption>(3){{add(option);}});
|
||||
else
|
||||
kindToOptionList.get(entryKind).add(option);
|
||||
}
|
||||
} catch (BuildException e) {
|
||||
}
|
||||
}
|
||||
|
||||
IKindBasedInfo infos[] = fKindToOptionArrayStore.getContents();
|
||||
IKindBasedInfo info;
|
||||
for(int i = 0; i < infos.length; i++){
|
||||
info = infos[i];
|
||||
List list = (List)info.getInfo();
|
||||
|
||||
IKindBasedInfo<IOption[]>[] infos = fKindToOptionArrayStore.getContents();
|
||||
for (IKindBasedInfo<IOption[]> info : infos) {
|
||||
List<IOption> list = kindToOptionList.get(info.getKind());
|
||||
if(list != null){
|
||||
IOption[] opts = (IOption[])list.toArray(new IOption[list.size()]);
|
||||
IOption[] opts = list.toArray(new IOption[list.size()]);
|
||||
info.setInfo(opts);
|
||||
} else {
|
||||
info.setInfo(EMPTY_OPTION_ARRAY);
|
||||
|
@ -244,26 +261,26 @@ public class BuildLanguageData extends CLanguageData {
|
|||
|
||||
private void calculateKindToUndefOptionArrayStore(){
|
||||
fKindToUndefOptionArrayStore.clear();
|
||||
Map<Integer, List<IOption>> kindToOptionList = new HashMap<Integer, List<IOption>>();
|
||||
IOption options[] = fTool.getOptions();
|
||||
for(int i = 0; i < options.length; i++){
|
||||
IOption option = options[i];
|
||||
for (final IOption option : options) {
|
||||
try {
|
||||
int type = option.getValueType();
|
||||
int entryKind = ManagedBuildManager.optionUndefTypeToEntryKind(type);
|
||||
Integer entryKind = ManagedBuildManager.optionUndefTypeToEntryKind(option.getValueType());
|
||||
if(entryKind != 0){
|
||||
getOptionList(fKindToUndefOptionArrayStore, entryKind).add(option);
|
||||
if (!kindToOptionList.containsKey(entryKind))
|
||||
kindToOptionList.put(entryKind, new ArrayList<IOption>(3){{add(option);}});
|
||||
else
|
||||
kindToOptionList.get(entryKind).add(option);
|
||||
}
|
||||
} catch (BuildException e) {
|
||||
}
|
||||
}
|
||||
|
||||
IKindBasedInfo infos[] = fKindToUndefOptionArrayStore.getContents();
|
||||
IKindBasedInfo info;
|
||||
for(int i = 0; i < infos.length; i++){
|
||||
info = infos[i];
|
||||
List list = (List)info.getInfo();
|
||||
|
||||
IKindBasedInfo<IOption[]>[] infos = fKindToUndefOptionArrayStore.getContents();
|
||||
for (IKindBasedInfo<IOption[]> info : infos) {
|
||||
List<IOption> list = kindToOptionList.get(info.getKind());
|
||||
if(list != null){
|
||||
IOption[] opts = (IOption[])list.toArray(new IOption[list.size()]);
|
||||
IOption[] opts = list.toArray(new IOption[list.size()]);
|
||||
info.setInfo(opts);
|
||||
} else {
|
||||
info.setInfo(EMPTY_OPTION_ARRAY);
|
||||
|
@ -273,13 +290,13 @@ public class BuildLanguageData extends CLanguageData {
|
|||
|
||||
|
||||
IOption[] getUndefOptionsForKind(int entryKind){
|
||||
KindBasedStore store = getKindToUndefOptionArrayStore();
|
||||
return (IOption[])store.get(entryKind);
|
||||
KindBasedStore<IOption[]> store = getKindToUndefOptionArrayStore();
|
||||
return store.get(entryKind);
|
||||
}
|
||||
|
||||
IOption[] getOptionsForKind(int entryKind){
|
||||
KindBasedStore store = getKindToOptionArrayStore();
|
||||
return (IOption[])store.get(entryKind);
|
||||
KindBasedStore<IOption[]> store = getKindToOptionArrayStore();
|
||||
return store.get(entryKind);
|
||||
}
|
||||
|
||||
/* private IOption[] getOptionsForType(int type){
|
||||
|
@ -288,14 +305,6 @@ public class BuildLanguageData extends CLanguageData {
|
|||
|
||||
}
|
||||
*/
|
||||
private List getOptionList(KindBasedStore store, int kind){
|
||||
List list = (List)store.get(kind);
|
||||
if(list == null){
|
||||
list = new ArrayList();
|
||||
store.put(kind, list);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setLanguageId(String id) {
|
||||
if(CDataUtil.objectsEqual(id, fInputType.getLanguageId(fTool))){
|
||||
|
@ -371,8 +380,8 @@ public class BuildLanguageData extends CLanguageData {
|
|||
public void setSourceContentTypeIds(String[] ids) {
|
||||
String[] headerIds = fInputType.getHeaderContentTypeIds();
|
||||
|
||||
List newSrc = new ArrayList(ids.length);
|
||||
List newHeaders = new ArrayList(ids.length);
|
||||
List<String> newSrc = new ArrayList<String>(ids.length);
|
||||
List<String> newHeaders = new ArrayList<String>(ids.length);
|
||||
for(int i = 0; i < ids.length; i++){
|
||||
String id = ids[i];
|
||||
int j = 0;
|
||||
|
@ -387,8 +396,8 @@ public class BuildLanguageData extends CLanguageData {
|
|||
}
|
||||
}
|
||||
|
||||
String newSrcIds[] = (String[])newSrc.toArray(new String[newSrc.size()]);
|
||||
String newHeaderIds[] = (String[])newHeaders.toArray(new String[newHeaders.size()]);
|
||||
String newSrcIds[] = newSrc.toArray(new String[newSrc.size()]);
|
||||
String newHeaderIds[] = newHeaders.toArray(new String[newHeaders.size()]);
|
||||
|
||||
if(!Arrays.equals(newSrcIds, fInputType.getSourceContentTypeIds())){
|
||||
// fInputType = fTool.getEdtableInputType(fInputType);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2007, 2008 Intel Corporation and others.
|
||||
* Copyright (c) 2007, 2009 Intel Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
|
@ -8,6 +8,7 @@
|
|||
* Contributors:
|
||||
* Intel Corporation - Initial API and implementation
|
||||
* IBM Corporation
|
||||
* James Blackburn (Broadcom Corp.)
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.managedbuilder.ui.properties;
|
||||
|
||||
|
@ -543,13 +544,15 @@ public class NewCfgDialog implements INewCfgDialog {
|
|||
}
|
||||
if (cfgDes != null) {
|
||||
config.setConfigurationDescription(cfgDes);
|
||||
config.exportArtifactInfo();
|
||||
config.setName(newName);
|
||||
config.setDescription(newDescription);
|
||||
|
||||
String target = config.getArtifactName();
|
||||
if (target == null || target.length() == 0)
|
||||
config.setArtifactName(mp.getDefaultArtifactName());
|
||||
|
||||
// Export artifact info as needed by project references
|
||||
config.exportArtifactInfo();
|
||||
}
|
||||
}
|
||||
if (config == null || cfgDes == null) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2007 Intel Corporation and others.
|
||||
* Copyright (c) 2007, 2009 Intel Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
|
@ -14,6 +14,17 @@ import org.eclipse.cdt.core.settings.model.CExternalSetting;
|
|||
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
|
||||
/**
|
||||
* Abstract base class for the External Settings Provider extension point. Contributed
|
||||
* external settings are added to the Project's build configuration.
|
||||
*/
|
||||
public abstract class CExternalSettingProvider {
|
||||
|
||||
/**
|
||||
* Hook for fetching external settings from the contributed external setting provider
|
||||
* @param project
|
||||
* @param cfg ICConfigurationDescription for which to fetch contributed external settings
|
||||
* @return CExternalSetting[] or contributed external settings
|
||||
*/
|
||||
public abstract CExternalSetting[] getSettings(IProject project, ICConfigurationDescription cfg);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2007 Intel Corporation and others.
|
||||
* Copyright (c) 2007, 2009 Intel Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
|
@ -7,13 +7,33 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Intel Corporation - Initial API and implementation
|
||||
* James Blackburn (Broadcom Corp.)
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.core.settings.model.util;
|
||||
|
||||
public interface IKindBasedInfo {
|
||||
import org.eclipse.cdt.core.settings.model.ICSettingEntry;
|
||||
|
||||
/**
|
||||
* Type-Parameterised kind based item
|
||||
* @param <T>
|
||||
*/
|
||||
public interface IKindBasedInfo<T> {
|
||||
/**
|
||||
* @return {@link ICSettingEntry} type
|
||||
*/
|
||||
int getKind();
|
||||
|
||||
Object getInfo();
|
||||
|
||||
Object setInfo(Object newInfo);
|
||||
|
||||
/**
|
||||
* Return type info
|
||||
* @see KindBasedStore
|
||||
* @return the data stored
|
||||
*/
|
||||
T getInfo();
|
||||
|
||||
/**
|
||||
* Set info
|
||||
* @param newInfo
|
||||
* @return previous data stored
|
||||
*/
|
||||
T setInfo(T newInfo);
|
||||
}
|
||||
|
|
|
@ -34,11 +34,29 @@ import org.eclipse.cdt.core.settings.model.util.KindBasedStore;
|
|||
import org.eclipse.cdt.internal.core.settings.model.CExternalSettinsDeltaCalculator.ExtSettingsDelta;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
|
||||
/**
|
||||
* Responsible for applying external settings delta to a given ICConfigurationDescrptions
|
||||
*/
|
||||
public class CExternalSettingsDeltaProcessor {
|
||||
|
||||
/**
|
||||
* Main entrance point for applying a full array of external settings delta
|
||||
* @param des ICConfigurationDescription
|
||||
* @param deltas ExtSettingsDelta array
|
||||
* @return boolean indicating whether there was change
|
||||
*/
|
||||
static boolean applyDelta(ICConfigurationDescription des, ExtSettingsDelta deltas[]){
|
||||
return applyDelta(des, deltas, KindBasedStore.ORED_ALL_ENTRY_KINDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the deltas to all resource description (overriden resource configs)
|
||||
* in the configuration description
|
||||
* @param des The configuration description to be updated
|
||||
* @param deltas deltas to be applied
|
||||
* @param kindMask
|
||||
* @return
|
||||
*/
|
||||
static boolean applyDelta(ICConfigurationDescription des, ExtSettingsDelta deltas[], int kindMask){
|
||||
ICResourceDescription rcDess[] = des.getResourceDescriptions();
|
||||
boolean changed = false;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2007, 2008 Intel Corporation and others.
|
||||
* Copyright (c) 2007, 2009 Intel Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
|
@ -29,6 +29,9 @@ import org.eclipse.core.runtime.IExtensionPoint;
|
|||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
|
||||
/**
|
||||
* Responsible for managing external settings providers contributed through the extension point
|
||||
*/
|
||||
public class ExtensionContainerFactory extends CExternalSettingContainerFactoryWithListener {
|
||||
static final String FACTORY_ID = CCorePlugin.PLUGIN_ID + ".extension.container.factory"; //$NON-NLS-1$
|
||||
private static final String EXTENSION_ID = CCorePlugin.PLUGIN_ID + ".externalSettingsProvider"; //$NON-NLS-1$
|
||||
|
@ -80,6 +83,7 @@ public class ExtensionContainerFactory extends CExternalSettingContainerFactoryW
|
|||
return fId;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String getName(){
|
||||
return fName;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue